Deref
Трейт
Deref, позволяет изменить поведение оператора разыменования*
Реализовав
Derefтаким образом, что умный указатель может рассматриваться как обычная ссылка, вы можете писать код, оперирующий ссылками, а также использовать этот код с умными указателями.
Разыменование типа
let y = &x;— создали ссылкуassert_eq!(5, *y);— разыменовали ссылкуlet y = Box::new(x);это идентичноlet y = &x;и там и там ссылка
Повторим Box<T>
- Создадим структуру
- Напишем конструктор new
#![allow(unused)] fn main() { struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } } }
- Определим метод
defer
#![allow(unused)] fn main() { use std::ops::Deref; impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } }
Все! Теперь assert_eq!(5, *y) разыменовывает аналогично.
Неявные разыменованные приведения с функциями и методами
Например,
derefcoercion может преобразовать&Stringв&str, потому чтоStringреализует признакDeref, который возвращает&str.
Типаж DerefMut для переопределения оператора * у изменяемых ссылок.
Rust выполняет разыменованное приведение, когда находит типы и реализации типажей в трёх случаях:
- Из типа
&Tв тип&Uкогда верноT: Deref<Target=U> - Из типа
&mut Tв тип&mut Uкогда верноT: DerefMut<Target=U> - Из типа
&mut Tв тип&Uкогда верноT: Deref<Target=U>
Struct-кортежи (tuple structs) - это гибрид между обычными структурами и кортежами.
Основные особенности:
1. Синтаксис объявления
#![allow(unused)] fn main() { // Обычная структура struct Point { x: i32, y: i32, } // Struct-кортеж struct PointTuple(i32, i32); // Struct-кортеж с одним полем (новый тип) struct Meters(f64); }
2. Создание экземпляров
fn main() { // Обычная структура let p1 = Point { x: 10, y: 20 }; // Struct-кортеж let p2 = PointTuple(10, 20); let distance = Meters(5.5); }
3. Доступ к полям
fn main() { let point = PointTuple(10, 20); let meters = Meters(5.5); // Доступ по индексу (как у кортежей) println!("x = {}", point.0); println!("y = {}", point.1); println!("meters = {}", meters.0); }
Отличия от других структур:
От обычных структур (struct):
struct Regular { x: i32, y: i32 } struct TupleStruct(i32, i32); fn main() { let regular = Regular { x: 1, y: 2 }; let tuple_struct = TupleStruct(1, 2); // Обычная структура - доступ по имени println!("{}", regular.x); // Struct-кортеж - доступ по индексу println!("{}", tuple_struct.0); }
От кортежей (tuple):
fn main() { // Обычный кортеж let tuple: (i32, i32) = (10, 20); // Struct-кортеж struct Point(i32, i32); let point = Point(10, 20); // Кортеж - анонимный тип // Struct-кортеж - именованный тип }
Практические примеры:
1. Создание новых типов (Newtype pattern)
struct UserId(u64); struct Email(String); fn create_user(id: UserId, email: Email) { println!("User {}: {}", id.0, email.0); } fn main() { let id = UserId(12345); let email = Email("user@example.com".to_string()); create_user(id, email); }
2. Группировка связанных данных
struct Color(u8, u8, u8); struct Bounds(usize, usize); fn main() { let red = Color(255, 0, 0); let screen = Bounds(1920, 1080); println!("RGB: {}, {}, {}", red.0, red.1, red.2); println!("Resolution: {}x{}", screen.0, screen.1); }
3. Pattern matching
struct Point(i32, i32); fn check_point(point: Point) { match point { Point(0, 0) => println!("Origin"), Point(x, 0) => println!("On x-axis at {}", x), Point(0, y) => println!("On y-axis at {}", y), Point(x, y) => println!("At ({}, {})", x, y), } } fn main() { check_point(Point(0, 0)); check_point(Point(5, 0)); check_point(Point(3, 4)); }
Преимущества struct-кортежей:
- Лёгкость написания - меньше boilerplate кода
- Newtype pattern - создание семантически новых типов
- Удобство для простых агрегатов - когда имена полей не важны
- Совместимость с кортежами - можно использовать в pattern matching
Когда использовать:
- Для создания новых типов (newtype)
- Когда данные логически связаны, но именованные поля избыточны
- Для простых контейнеров с небольшим количеством полей
- Когда нужен именованный тип, но синтаксис кортежа удобнее