Неявные преобразования Deref для функций и методов
Преобразование Deref преобразует ссылку на тип, реализующий трейт Deref
, в ссылку на другой тип. Например, преобразование Deref может преобразовать &String
в &str
, потому что String
реализует трейт Deref
так, что возвращает &str
. Преобразование Deref - это удобство, которое Rust выполняет для аргументов функций и методов, и работает только для типов, реализующих трейт Deref
. Это происходит автоматически, когда мы передаем ссылку на значение определенного типа в качестве аргумента в функцию или метод, который не соответствует типу параметра в определении функции или метода. Последовательность вызовов метода deref
преобразует тип, который мы предоставили, в тип, который требуется параметру.
Преобразование Deref было добавлено в Rust, чтобы программисты, пишущие вызовы функций и методов, не нужно было добавлять столько явных ссылок и операций дереференцирования с помощью &
и *
. Функция преобразования Deref также позволяет нам писать больше кода, который может работать как для ссылок, так и для умных указателей.
Для того, чтобы увидеть преобразование Deref в действии, давайте используем тип MyBox<T>
, который мы определили в Listing 15-8, а также реализацию Deref
, которую мы добавили в Listing 15-10. Listing 15-11 показывает определение функции, которая имеет параметр строкового среза.
Filename: src/main.rs
fn hello(name: &str) {
println!("Hello, {name}!");
}
Listing 15-11: Функция hello
, которая имеет параметр name
типа &str
Мы можем вызвать функцию hello
с аргументом - строковым срезов, например, hello("Rust");
. Преобразование Deref позволяет вызвать hello
с ссылкой на значение типа MyBox<String>
, как показано в Listing 15-12.
Filename: src/main.rs
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
Listing 15-12: Вызов hello
с ссылкой на значение MyBox<String>
, который работает благодаря преобразованию Deref
Здесь мы вызываем функцию hello
с аргументом &m
, который является ссылкой на значение MyBox<String>
. Поскольку мы реализовали трейт Deref
для MyBox<T>
в Listing 15-10, Rust может преобразовать &MyBox<String>
в &String
, вызвав deref
. Стандартная библиотека предоставляет реализацию Deref
для String
, которая возвращает строковый срез, и это описано в документации API для Deref
. Rust снова вызывает deref
, чтобы преобразовать &String
в &str
, что соответствует определению функции hello
.
Если Rust не реализовывал преобразование Deref, мы бы不得不 написать код из Listing 15-13 вместо кода из Listing 15-12, чтобы вызвать hello
с значением типа &MyBox<String>
.
Filename: src/main.rs
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&(*m)[..]);
}
Listing 15-13: Код, который мы бы不得不 написать, если бы Rust не имел преобразования Deref
(*m)
дереференцирует MyBox<String>
в String
. Затем &
и [..]
берут строковый срез из String
, который равен всей строке, чтобы соответствовать сигнатуре hello
. Этот код без преобразований Deref труднее читать, писать и понимать, учитывая все эти символы. Преобразование Deref позволяет Rust автоматически обрабатывать эти преобразования для нас.
Когда трейт Deref
определен для типов, участвующих в этом, Rust анализирует типы и использует Deref::deref
столько раз, сколько необходимо, чтобы получить ссылку, соответствующую типу параметра. Количество раз, которое необходимо вставить Deref::deref
, определяется на этапе компиляции, поэтому нет накладных расходов во время выполнения за счет использования преобразования Deref!