Learn Rust Coding · Урок

Удобство Deref и обёрток

Сделайте обёртки естественными в использовании.

Урок 4 из 413 шагов

«Удобство Deref и обёрток» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.

Проблема удобства использования

Обёртывание типа в новый тип повышает безопасность, но снижает удобство: внезапно приходится повсюду писать wrapper.0.method() и вручную заново реализовывать методы.

Трейт Deref может вернуть значительную часть этого удобства, позволяя оболочке вести себя как хранимое в ней значение.

Что делает Deref

Deref определяет результат выражения *value и обеспечивает работу оператора .. Когда Вы вызываете метод, которого нет у оболочки, компилятор повторяет попытку для цели разыменования.

Этот автоматический шаг называется приведением при разыменовании.

use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
    type Target = T;
    fn deref(&self) -> &T { &self.0 }
}

Приведение при разыменовании на практике

После реализации Deref вызов внутренних методов через оболочку просто работает. Компилятор вставляет .deref() за Вас, пока не находит нужный метод.

use std::ops::Deref;
struct Name(String);
impl Deref for Name {
    type Target = String;
    fn deref(&self) -> &String { &self.0 }
}
fn main() {
    let n = Name("rustacean".to_string());
    println!("len = {}", n.len()); // String::len via deref
}

DerefMut для изменяемого доступа

Deref предоставляет только общий доступ. Чтобы через оболочку вызывать методы, которым нужен &mut self, также реализуйте DerefMut, возвращающий изменяемую ссылку на внутреннее значение.

use std::ops::{Deref, DerefMut};
struct Stack(Vec<i32>);
impl Deref for Stack {
    type Target = Vec<i32>;
    fn deref(&self) -> &Vec<i32> { &self.0 }
}
impl DerefMut for Stack {
    fn deref_mut(&mut self) -> &mut Vec<i32> { &mut self.0 }
}

Приведение на границах функций

Приведение при разыменовании применяется и к аргументам. &Name приводится к &String, а тот, в свою очередь, к &str, поэтому оболочку можно напрямую передавать в функции, ожидающие ссылку на внутреннее значение.

fn greet(who: &str) {
    println!("hi {}", who);
}
// greet(&name) works: &Name -> &String -> &str

Рабочий пример приведения

Эта программа передаёт ссылку на оболочку туда, где ожидается срез строки. Цепочка приведений при разыменовании происходит незаметно в месте вызова.

use std::ops::Deref;
struct Tag(String);
impl Deref for Tag {
    type Target = String;
    fn deref(&self) -> &String { &self.0 }
}
fn shout(s: &str) { println!("{}!", s.to_uppercase()); }
fn main() {
    let t = Tag("ship".to_string());
    shout(&t);
}

Не злоупотребляйте Deref

Рекомендации по API Rust предостерегают от реализации Deref для типов, которые не являются настоящими интеллектуальными указателями. Чрезмерное использование делает доступные оболочке методы непредсказуемыми и удивляет читателей.

Если новый тип создан для ограничения API, безусловная реализация Deref может раскрыть именно те методы, которые Вы собирались скрыть.

AsRef как целенаправленная альтернатива

Если Вам нужно явное преобразование, а не автоматическое приведение, реализуйте AsRef. Вызывающий код намеренно пишет .as_ref(), благодаря чему собственная поверхность оболочки остаётся понятной.

struct Path(String);
impl AsRef<str> for Path {
    fn as_ref(&self) -> &str { &self.0 }
}

Передача выбранных методов

Для ограничивающей оболочки лучше вручную реализовать только те методы, которые Вы хотите предоставить. Так Вы полностью контролируете API и не раскрываете всё содержимое через Deref.

struct Counter(Vec<u8>);
impl Counter {
    fn len(&self) -> usize { self.0.len() }
    fn push(&mut self, b: u8) { self.0.push(b); }
}

Интеллектуальные указатели используют Deref

Стандартные интеллектуальные указатели, такие как Box, Rc и Arc, реализуют Deref. Поэтому Вы можете прозрачно вызывать их внутренние методы. Это законное и предусмотренное применение трейта.

use std::rc::Rc;
fn main() {
    let shared = Rc::new(String::from("data"));
    // Deref lets us call String methods directly
    println!("len = {}", shared.len());
}

Выбор стратегии удобства

Выбирайте подход исходя из назначения. Используйте Deref для настоящих оболочек интеллектуальных указателей, AsRef для явных преобразований заимствованных значений, а ручную передачу методов — когда новый тип должен ограничивать API внутреннего типа.

Неправильный выбор либо скрывает слишком много, либо раскрывает слишком много.

Быстрая проверка

Определите, для какой оболочки стоит реализовать Deref.

Итоги

Deref и приведение при разыменовании возвращают оболочкам удобство использования: оператор . и аргументы-ссылки передаются внутреннему значению; DerefMut добавляет изменяемый доступ.

Используйте Deref только для настоящих интеллектуальных указателей. Для ограничивающих новых типов применяйте AsRef для явных преобразований или передавайте вручную только те методы, которые хотите предоставить.

Можно начать бесплатно

Изучай Rust с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
39
Уроки
144

Часто задаваемые вопросы

Урок «Удобство Deref и обёрток» бесплатный?

Да — полный текст урока «Удобство Deref и обёрток» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.

Чему я научусь в уроке «Удобство Deref и обёрток»?

Сделайте обёртки естественными в использовании. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Learn Rust Coding?

Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Удобство Deref и обёрток»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Learn Rust Coding?

Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Шаблон Builder
  2. Шаблон Newtype
  3. Builder с состоянием типа
  4. Удобство Deref и обёрток
← Назад к Learn Rust Coding