Deref

Трейт Deref, позволяет изменить поведение оператора разыменования *

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

Разыменование типа

  1. let y = &x; — создали ссылку
  2. assert_eq!(5, *y); — разыменовали ссылку
  3. let y = Box::new(x); это идентично let y = &x; и там и там ссылка

Повторим Box<T>

  1. Создадим структуру
  2. Напишем конструктор new
#![allow(unused)]
fn main() {
struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
}
  1. Определим метод 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) разыменовывает аналогично.

Неявные разыменованные приведения с функциями и методами

Например, deref coercion может преобразовать &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-кортежей:

  1. Лёгкость написания - меньше boilerplate кода
  2. Newtype pattern - создание семантически новых типов
  3. Удобство для простых агрегатов - когда имена полей не важны
  4. Совместимость с кортежами - можно использовать в pattern matching

Когда использовать:

  • Для создания новых типов (newtype)
  • Когда данные логически связаны, но именованные поля избыточны
  • Для простых контейнеров с небольшим количеством полей
  • Когда нужен именованный тип, но синтаксис кортежа удобнее