searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

rust Trait对象学习笔记

2025-10-29 10:31:58
0
0

1. Trait 对象

1.1 什么是Trait对象

pub trait Draw {
    fn draw(&self);
}

pub struct Screen {
    // Box<dyn Draw> 就是一个 trait 对象
    // 它指向一个实现了 Draw trait 的类型的实例
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 动态分发
        }
    }
}

关键特点:

  • dyn Draw 表示"任何实现了 Draw trait 的类型"
  • 使用 Box 在堆上分配,因为 trait 对象的大小在编译时未知
  • 允许在同一个集合中存储不同类型但实现相同 trait 的对象

2. Trait对象vs泛型

2.1 使用Trait对象(动态分发)

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>, // 可以存储多种类型
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 动态分发
        }
    }
}

特点:

  • 运行时通过虚函数表(vtable)查找方法
  • 有轻微的性能开销
  • 但更灵活,支持异构集合

2.2 使用泛型(静态分发)

pub struct Screen2<T: Draw> {
    // 这里限制了 Vec 中必须是实现了 Draw 的同类型对象
    // 比如 vec 里边全是 Button 类型的对象
    pub components: Vec<T>,
}

impl<T> Screen2<T>
where 
    T: Draw 
{
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 静态分发
        }
    }
}

特点:

  • 编译时进行单态化(monomorphization)
  • 为每个具体类型生成专用代码
  • 性能更好(无运行时开销)

3. 对象安全(Object Safety)

3.1 对象安全的规则

一个 trait 是对象安全的,当且仅当它的所有方法满足:

  • 返回值类型不是 Self
  • 方法没有任何泛型类型参数
// 对象安全的 trait
pub trait Draw {
    fn draw(&self);                    // 返回 (),不是 Self
    fn get_size(&self) -> (u32, u32);  // 返回具体类型,不是 Self
    // 没有泛型参数
}

3.2 非对象安全的例子

// 非对象安全的 trait
pub trait Clone {
    fn clone(&self) -> Self;  // 返回 Self,违反规则1
}

pub trait Serialize {
    fn serialize<T: Writer>(&self, writer: &mut T);  // 有泛型参数,违反规则2
}

// 以下代码会编译错误:
// pub struct Screen {
//     pub components: Vec<Box<dyn Clone>>,  // 错误!Clone 不是对象安全的
// }

4. 选择

4.1 何时使用 Trait 对象

适合使用 Trait 对象的情况:

  • 需要存储不同类型的对象在同一个集合中
  • 关注代码灵活性胜过极致性能
  • trait 的方法不多,调用频率不高

4.2 何时使用泛型

适合使用泛型的情况:

  • 性能是关键考虑因素
  • 集合中只需要存储单一类型
  • 方法调用频率很高
0条评论
作者已关闭评论
陈****然
6文章数
0粉丝数
陈****然
6 文章 | 0 粉丝
陈****然
6文章数
0粉丝数
陈****然
6 文章 | 0 粉丝
原创

rust Trait对象学习笔记

2025-10-29 10:31:58
0
0

1. Trait 对象

1.1 什么是Trait对象

pub trait Draw {
    fn draw(&self);
}

pub struct Screen {
    // Box<dyn Draw> 就是一个 trait 对象
    // 它指向一个实现了 Draw trait 的类型的实例
    pub components: Vec<Box<dyn Draw>>,
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 动态分发
        }
    }
}

关键特点:

  • dyn Draw 表示"任何实现了 Draw trait 的类型"
  • 使用 Box 在堆上分配,因为 trait 对象的大小在编译时未知
  • 允许在同一个集合中存储不同类型但实现相同 trait 的对象

2. Trait对象vs泛型

2.1 使用Trait对象(动态分发)

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>, // 可以存储多种类型
}

impl Screen {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 动态分发
        }
    }
}

特点:

  • 运行时通过虚函数表(vtable)查找方法
  • 有轻微的性能开销
  • 但更灵活,支持异构集合

2.2 使用泛型(静态分发)

pub struct Screen2<T: Draw> {
    // 这里限制了 Vec 中必须是实现了 Draw 的同类型对象
    // 比如 vec 里边全是 Button 类型的对象
    pub components: Vec<T>,
}

impl<T> Screen2<T>
where 
    T: Draw 
{
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw(); // 静态分发
        }
    }
}

特点:

  • 编译时进行单态化(monomorphization)
  • 为每个具体类型生成专用代码
  • 性能更好(无运行时开销)

3. 对象安全(Object Safety)

3.1 对象安全的规则

一个 trait 是对象安全的,当且仅当它的所有方法满足:

  • 返回值类型不是 Self
  • 方法没有任何泛型类型参数
// 对象安全的 trait
pub trait Draw {
    fn draw(&self);                    // 返回 (),不是 Self
    fn get_size(&self) -> (u32, u32);  // 返回具体类型,不是 Self
    // 没有泛型参数
}

3.2 非对象安全的例子

// 非对象安全的 trait
pub trait Clone {
    fn clone(&self) -> Self;  // 返回 Self,违反规则1
}

pub trait Serialize {
    fn serialize<T: Writer>(&self, writer: &mut T);  // 有泛型参数,违反规则2
}

// 以下代码会编译错误:
// pub struct Screen {
//     pub components: Vec<Box<dyn Clone>>,  // 错误!Clone 不是对象安全的
// }

4. 选择

4.1 何时使用 Trait 对象

适合使用 Trait 对象的情况:

  • 需要存储不同类型的对象在同一个集合中
  • 关注代码灵活性胜过极致性能
  • trait 的方法不多,调用频率不高

4.2 何时使用泛型

适合使用泛型的情况:

  • 性能是关键考虑因素
  • 集合中只需要存储单一类型
  • 方法调用频率很高
文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0