Generics and traits II

04 ноември 2025

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Асоциирани типове

Продължение

Асоциирани типове

Позволяват задаване на различен тип за всяка имплементрация на trait-а

1 2 3 4 5
trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

Асоциирани типове

1 2 3 4 5 6 7 8 9 10 11
impl Iterator for std::str::Chars {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> { ... }
}

impl Iterator for std::str::Bytes {
    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> { ... }
}

Асоциирани типове

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
fn first_and_last<I: Iterator>(iter: I) -> Option<???> {
    let mut iter = iter;

    let first = match iter.next() {
        Some(elem) => elem,
        None => return None,
    };

    let last = match iter.last() {
        Some(elem) => elem,
        None => return None,
    };

    Some((first, last))
}

Асоциирани типове

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
fn first_and_last<I: Iterator>(iter: I) -> Option<(I::Item, I::Item)> {
    let mut iter = iter;

    let first = match iter.next() {
        Some(elem) => elem,
        None => return None,
    };

    let last = match iter.last() {
        Some(elem) => elem,
        None => return None,
    };

    Some((first, last))
}
fn main() {}
fn first_and_last(iter: I) -> Option<(I::Item, I::Item)> {
    let mut iter = iter;

    let first = match iter.next() {
        Some(elem) => elem,
        None => return None,
    };

    let last = match iter.last() {
        Some(elem) => elem,
        None => return None,
    };

    Some((first, last))
}

Асоциирани типове

1 2 3 4 5 6
fn print_all<I: Iterator>(iter: I) {
    for elem in iter {
        print!("{:?} ", elem);
    }
    println!();
}
error[E0277]: `<I as Iterator>::Item` doesn't implement `Debug` --> src/bin/main_a932cd0158a8f5da3a503b6d3f087743cafb2b95.rs:5:25 | 5 | print!("{:?} ", elem); | ---- ^^^^ `<I as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug` | | | required by this formatting parameter | = help: the trait `Debug` is not implemented for `<I as Iterator>::Item` = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting the associated type | 3 | fn print_all<I: Iterator>(iter: I) where <I as Iterator>::Item: Debug { | ++++++++++++++++++++++++++++++++++ For more information about this error, try `rustc --explain E0277`. error: could not compile `rust` (bin "main_a932cd0158a8f5da3a503b6d3f087743cafb2b95") due to 1 previous error
fn main() {}
fn print_all(iter: I) {
    for elem in iter {
        print!("{:?} ", elem);
    }
    println!();
}

Асоциирани типове

1 2 3 4 5 6 7 8 9
fn print_all<I: Iterator>(iter: I)
where
    I::Item: std::fmt::Debug
{
    for elem in iter {
        print!("{:?} ", elem);
    }
    println!();
}
fn main() {}
fn print_all(iter: I)
where
    I::Item: std::fmt::Debug
{
    for elem in iter {
        print!("{:?} ", elem);
    }
    println!();
}

Асоциирани типове

1 2 3 4 5 6 7 8
fn sum<I: Iterator<Item=i32>>(iter: I) -> i32 {
    let mut sum = 0;

    for elem in iter {
        sum += elem;
    }
    sum
}
fn main() {}
fn sum>(iter: I) -> i32 {
    let mut sum = 0;

    for elem in iter {
        sum += elem;
    }
    sum
}

Асоциирани типове

1 2 3 4 5 6 7 8
fn sum(iter: &mut dyn Iterator<Item=i32>) -> i32 {
    let mut sum = 0;

    for elem in iter {
        sum += elem;
    }
    sum
}
fn main() {}
fn sum(iter: &mut dyn Iterator) -> i32 {
    let mut sum = 0;

    for elem in iter {
        sum += elem;
    }
    sum
}

Traits

пълно име

Когато напишем Type::item, това означава:

Traits

пълно име

Когато напишем Type::item, това означава:

Traits

пълно име

Когато напишем Type::item, това означава:

Traits

пълно име

Когато напишем Type::item, това означава:

Traits

пълно име

Ако има двусмислица - трябва да се използва пълното квалифицирано име

1
<Type as Trait>::item

Пример

1 2
<std::str::Chars as std::iter::Iterator>::Item  // char
<std::str::Chars as std::iter::Iterator>::next  // fn (&mut std::str::Chars) -> Option<char>

Box

Box

Box

Box

Box

1 2 3 4 5 6 7 8 9
struct User {
    name: String,
    age: i32,
}

let user = Box::new(User {
    name: String::from("Тодор"),
    age: 25,
});
fn main() {
struct User {
    name: String,
    age: i32,
}

let user = Box::new(User {
    name: String::from("Тодор"),
    age: 25,
});
}

Box

            Box<User>
            +–––––––+
stack frame │ ptr=• │
            +–––––│–+
              +–––+
              │
              │
            [–│–––––––––– User –––––––––––––]
            +–V–––––+–––––––+–––––––+–––––––+
       heap │         name          │  age  │
            +–––––––+–––––––+–––––––+–––––––+

Box

дереференциране

1 2 3 4 5 6 7 8 9
let x = Box::new(5);
let y = Box::new(10);

// error: Cannot add `Box<{integer}>` to `Box<{integer}>`
// println!("{}", x + y);

// OK
let sum = *x + *y;
println!("{} + {} = {}", x, y, sum);
5 + 10 = 15
fn main() {
let x = Box::new(5);
let y = Box::new(10);

// error: Cannot add `Box<{integer}>` to `Box<{integer}>`
// println!("{}", x + y);

// OK
let sum = *x + *y;
println!("{} + {} = {}", x, y, sum);
}

Box

дереференциране

1 2 3 4 5 6 7 8 9 10
let boxed_user = Box::new(User {
    /* ... */
});

let user = *boxed_user;  // user: User

println!("{:?}", user);

// error: borrow of moved value: `boxed_user`
// println!("{:?}", boxed_user);
User { name: "Тодор", age: 25 }
fn main() {
#[derive(Debug)] struct User { name: String, age: i32 }
let boxed_user = Box::new(User {
name: String::from("Тодор"),
age: 25,
    /* ... */
});

let user = *boxed_user;  // user: User

println!("{:?}", user);

// error: borrow of moved value: `boxed_user`
// println!("{:?}", boxed_user);
}

Box

дереференциране

1 2 3 4 5 6 7 8 9 10
let boxed_user = Box::new(User {
    /* ... */
});

let user = &*boxed_user;  // user: &User

println!("{:?}", user);

// Ok
println!("{:?}", boxed_user);
User { name: "Тодор", age: 25 } User { name: "Тодор", age: 25 }
fn main() {
#[derive(Debug)] struct User { name: String, age: i32 }
let boxed_user = Box::new(User {
name: String::from("Тодор"),
age: 25,
    /* ... */
});

let user = &*boxed_user;  // user: &User

println!("{:?}", user);

// Ok
println!("{:?}", boxed_user);
}

Box

методи

Методи могат да се извикват през Box

1 2 3 4 5 6 7 8 9 10 11
impl User {
    fn say_hi(&self) {
        println!("{}: {}", self.name, "здравей!");
    }
}

let boxed_user = Box::new(User {
    /* ... */
});

boxed_user.say_hi();
Тодор: здравей!
fn main() {
#[derive(Debug)] struct User { name: String, age: i32 }
impl User {
    fn say_hi(&self) {
        println!("{}: {}", self.name, "здравей!");
    }
}

let boxed_user = Box::new(User {
name: String::from("Тодор"),
age: 25,
    /* ... */
});

boxed_user.say_hi();
}

Box

методи и функции

Референция към вътрешността може да се вземе с &*boxed или &boxed

1 2 3 4 5 6 7 8 9 10 11
fn greet(user: &User) {
    println!("Здравей, {}!", user.name);
}

let boxed_user = Box::new(User {
    /* ... */
});

greet(&*boxed_user); // OK

greet(&boxed_user);  // OK, но защо?
Здравей, Тодор! Здравей, Тодор!
fn main() {
#[derive(Debug)] struct User { name: String, age: i32 }
fn greet(user: &User) {
    println!("Здравей, {}!", user.name);
}

let boxed_user = Box::new(User {
name: String::from("Тодор"),
age: 25,
    /* ... */
});

greet(&*boxed_user); // OK

greet(&boxed_user);  // OK, но защо?
}

Deref coersion

Deref coersion

Deref coersion

Deref coersion

std::ops::Deref

1 2 3 4 5 6
// std::ops::Deref
pub trait Deref {
    type Target: ?Sized;

    fn deref(&self) -> &Self::Target;
}

Deref coersion

std::ops::Deref

1 2 3 4 5 6
// std::ops::Deref
pub trait Deref {
    type Target: ?Sized;

    fn deref(&self) -> &Self::Target;
}

Deref coersion

std::ops::Deref

Примери

Deref coersion

std::ops::Deref

Примери

Deref е предвидено да се използва специално за типове, които се държат все едно са Target или &Target

Deref coersion

1 2 3 4
// std::ops::DerefMut
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Deref coersion

1 2 3 4
// std::ops::DerefMut
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Deref coersion

1 2 3 4
// std::ops::DerefMut
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Deref coersion

1 2 3 4
// std::ops::DerefMut
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Deref coersion

1 2 3 4
// std::ops::DerefMut
pub trait DerefMut: Deref {
    fn deref_mut(&mut self) -> &mut Self::Target;
}

Sized

Sized

Какво представлява ?Sized в дефиницията на Deref?

1 2 3 4 5 6
// std::ops::Deref
pub trait Deref {
    type Target: ?Sized;

    fn deref(&self) -> &Self::Target;
}

Sized

Sized

Sized

Sized

Sized

Има типове, които не са Sized

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Има типове, които не са Sized

Това са типове, които:

Sized

Защо?

1 2 3 4 5
impl Deref for String {
    type Target = ???;

    fn deref(&self) -> &Self::Target { /* ... */ }
}

Какъв трабва да е типа на Target?

Sized

Защо?

1 2 3 4 5
impl Deref for String {
    type Target = ???;

    fn deref(&self) -> &Self::Target { /* ... */ }
}

Какъв трабва да е типа на Target?

Sized

Защо?

1 2 3 4 5
impl Deref for String {
    type Target = ???;

    fn deref(&self) -> &Self::Target { /* ... */ }
}

Какъв трабва да е типа на Target?

Sized

Защо?

1 2 3 4 5
impl Deref for String {
    type Target = ???;

    fn deref(&self) -> &Self::Target { /* ... */ }
}

Какъв трабва да е типа на Target?

Sized

?Sized

1 2 3 4
fn foo<T>(x: T) {}

// е еквиваленто на
fn foo<T: Sized>(x: T) {}

Sized

?Sized

1 2 3 4
fn foo<T: ?Sized>(x: &T) {}

// error: `x` doesn't have a size known at compile-time
// fn foo<T: ?Sized>(x: T) {}

Sized

traits

1 2 3 4 5 6 7 8
trait Aggregate {
    type Output;

    // Нещо като `&[str]` не може да съшествува.
    // Затова трябва да добавим изискване `Self: Sized`.
    fn aggregate(data: &[Self]) -> Self::Output
        where Self: Sized;
}
fn main() {}
trait Aggregate {
    type Output;

    // Нещо като `&[str]` не може да съшествува.
    // Затова трябва да добавим изискване `Self: Sized`.
    fn aggregate(data: &[Self]) -> Self::Output
        where Self: Sized;
}

Sized

traits

1 2 3 4 5 6 7 8
trait Aggregate
where
    Self: Sized
{
    type Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}
fn main() {}
trait Aggregate
where
    Self: Sized
{
    type Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}

или

1 2 3 4 5
trait Aggregate: Sized {
    type Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}
fn main() {}
trait Aggregate: Sized {
    type Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}

Sized

trait objects

Забележка - построяването на trait object dyn MyTrait изисква Self: ?Sized

1 2 3 4 5 6 7 8 9
// Ако добавим ограничение на трейта, няма да можем
// да създадем `&dyn Aggregate`
trait Aggregate: Sized {
    type Output;

    fn score(&self) -> Self::Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}
fn main() {}
// Ако добавим ограничение на трейта, няма да можем
// да създадем `&dyn Aggregate`
trait Aggregate: Sized {
    type Output;

    fn score(&self) -> Self::Output;

    fn aggregate(data: &[Self]) -> Self::Output;
}

Sized

trait objects

Забележка - построяването на trait object dyn MyTrait изисква Self: ?Sized

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Ако добавим ограничение на функцията, то ще можем
// да създадем `&dyn Aggregate`, но през trait object-а няма
// да можем да използваме метода `aggregate` - само `score`.
trait Aggregate {
    type Output;

    fn score(&self) -> Self::Output;

    fn aggregate(data: &[Self]) -> Self::Output 
        where Self: Sized;
}

// Ако искаме да използваме метода `aggregate`, трябва да използваме
// функция с generic параметър
fn my_aggregation1<T: Aggregate + Sized>(data: &[T]) -> T::Output { todo!() }

// Но припомняне, че Sized е имплицитно
fn my_aggregation2<T: Aggregate>(data: &[T]) -> T::Output { todo!() }
fn main() {}
// Ако добавим ограничение на функцията, то ще можем
// да създадем `&dyn Aggregate`, но през trait object-а няма
// да можем да използваме метода `aggregate` - само `score`.
trait Aggregate {
    type Output;

    fn score(&self) -> Self::Output;

    fn aggregate(data: &[Self]) -> Self::Output 
        where Self: Sized;
}

// Ако искаме да използваме метода `aggregate`, трябва да използваме
// функция с generic параметър
fn my_aggregation1(data: &[T]) -> T::Output { todo!() }

// Но припомняне, че Sized е имплицитно
fn my_aggregation2(data: &[T]) -> T::Output { todo!() }

Boxed

fat pointers

Boxed

traits

1 2 3 4 5 6 7 8 9 10 11 12
let mut log_values: Vec<Box<dyn std::fmt::Debug>> = vec![];

log_values.push(Box::new(5));
log_values.push(Box::new("my stirng"));

let boxed_user = Box::new(User {
    /* ... */
});

log_values.push(boxed_user);

println!("{:?}", log_values);
[5, "my stirng", User { name: "Тодор", age: 25 }]
fn main() {
#[derive(Debug)] struct User { name: String, age: i32 }

let mut log_values: Vec> = vec![];

log_values.push(Box::new(5));
log_values.push(Box::new("my stirng"));

let boxed_user = Box::new(User {
name: String::from("Тодор"),
age: 25,
    /* ... */
});

log_values.push(boxed_user);

println!("{:?}", log_values);
}

Boxed

traits

Предифиниране на оператори

Предифиниране на оператори

1 2 3 4 5
let x = a + b;

// се свежда до

let x = std::ops::Add::add(&a, &b);

Предифиниране на оператори

аритметични

Модул std::ops

Предифиниране на оператори

побитови

Модул std::ops

Предифиниране на оператори

сравнение

Модул std::cmp

Предифиниране на оператори

други

Модул std::ops

Предифиниране на оператори

Add изглежда така. Защо?

1 2 3 4 5
pub trait Add<Rhs = Self> {
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}

Associated types vs generic traits

Каква е разликата?

Типаж може да има асоцииран тип

1 2 3 4
trait MyTrait {
    type Output;
    fn do_stuff(&self) -> Self::Output;
}

Или да има типов параметър

1 2 3
trait MyTrait<T> {
    fn do_stuff(&self) -> T;
}

Associated types vs generic traits

Асоцииран тип

1 2 3 4 5 6
trait MyTrait {
    type Output;
}

impl MyTrait for Foo { type Output = f32; }
impl MyTrait for Bar { type Output = String; }
fn main() {
trait MyTrait {
    type Output;
}

impl MyTrait for Foo { type Output = f32; }
impl MyTrait for Bar { type Output = String; }

struct Foo;
struct Bar;
}

Associated types vs generic traits

Типов параметър

1 2 3 4 5
trait MyTrait<T> {
}

impl MyTrait<i32> for Foo { }
impl MyTrait<String> for Foo { }
fn main() {
trait MyTrait {
}

impl MyTrait for Foo { }
impl MyTrait for Foo { }

struct Foo;
struct Bar;
}

Associated types vs generic traits

Оператори

Защо аритметичните оператори са generic по Rhs?

1 2
Vector2: Mul<f32>
Vector2: Mul<Vector2>

Associated types vs generic traits

Оператори

Защо за аритметичните оператори Output e асоцииран тип?

Associated types vs generic traits

Оператори

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
trait Mul<Rhs = Self> {
    type Output;

    fn mul(self, rhs: Rhs) -> Self::Output;
}

pub struct Vector2 {
    x: f32,
    y: f32,
}

// vec * scalar
impl Mul<f32> for Vector2 {
    type Output = Vector2;
    fn mul(self, scalar: f32) -> Vector2 { Vector2 { x: self.x * scalar, y: self.y * scalar } }  
}

// vec * vec
impl Mul<Vector2> for Vector2 {
    type Output = Vector2;
    fn mul(self, rhs: Vector2) -> Vector2 { Vector2 { x: self.x * rhs.x, y: self.y * rhs.y } }  
}
fn main() {}
trait Mul {
    type Output;

    fn mul(self, rhs: Rhs) -> Self::Output;
}

pub struct Vector2 {
    x: f32,
    y: f32,
}

// vec * scalar
impl Mul for Vector2 {
    type Output = Vector2;
    fn mul(self, scalar: f32) -> Vector2 { Vector2 { x: self.x * scalar, y: self.y * scalar } }  
}

// vec * vec
impl Mul for Vector2 {
    type Output = Vector2;
    fn mul(self, rhs: Vector2) -> Vector2 { Vector2 { x: self.x * rhs.x, y: self.y * rhs.y } }  
}

Функции и ламбди

TODO: ще добавя слайдовете скоро

Въпроси