Rust 语言从入门到实战
唐刚
Rust 语言中文社区联合创始人
5266 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 36 讲
Rust 语言从入门到实战
15
15
1.0x
00:00/00:00
登录|注册

10|再探trait:带类型参数的trait及trait object

你好,我是 Mike,今天我们继续学习 trait 相关知识。
回顾一下我们上一节课中类型参数出现的地方。
用 trait 对 T 作类型空间的约束,比如T: TraitA
blanket implementation 时的 T,比如 impl<T: TraitB> TraitA for T {}
函数里的 T 参数,比如 fn doit<T>(a: T) {}
你要注意区分不同位置的 T。它的基础意义都是类型参数,但是放在不同的位置其侧重的意义有所不同。
T: TraitA 里的 T 表示类型参数,强调“参数”,使用 TraitA 来削减它的类型空间。
impl<T: TraitB> TraitA for T {} 末尾的 T 更强调类型参数的“类型”部分,为某些类型实现 TraitA。
doit<T>(a: T) {} 中第二个 T 表示某种类型,更强调类型参数的“类型”部分。
这节课我们要讲的是另外一个东西,它里面也带 T 参数。我们一起来看一下,它与之前这几种形式有什么不同。

trait 上带类型参数

trait 上也是可以带类型参数的,形式像下面这样:
trait TraitA<T> {}
表示这个 trait 里面的函数或方法,可能会用到这个类型参数。在定义 trait 的时候,还没确定这个类型参数的具体类型。要等到 impl 甚至使用类型方法的时候,才会具体化这个 T 的具体类型。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Rust中带类型参数的trait及trait object是本文的重点。文章深入探讨了类型参数在trait中的不同位置及其侧重意义,详细讲解了带类型参数的trait的定义和实现方式,以及在实现时对类型参数的约束和具体化。通过具体实例展示了带类型参数的trait的威力,以及在同一类型上实现同名方法参数类型的多种形态。此外,还介绍了带类型参数的trait的默认实现方式,并指出了初学者可能遇到的困难。文章还指出了关联类型的具化和类型参数的默认类型指定的区别。最后,文章介绍了trait object的概念,通过具体示例和对比分析,清晰地展现了带类型参数的trait和关联类型的区别,为读者提供了深入的技术视角。整体而言,本文全面介绍了带类型参数的trait及其相关知识,为读者提供了深入的了解和学习指导。文章还讨论了trait object、impl trait,以及使用枚举对类型进行聚合这三种方式之间的区别。类型系统(类型+ trait)是Rust的大脑,读者可以通过本文的内容多加练习,熟悉它的形式,掌握它的用法。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Rust 语言从入门到实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(15)

  • 最新
  • 精选
  • 哄哄
    rust生命周期的独特设计,导致了该语言需要设计一些处理方式应对特殊情况,比如生命周期的标注(主要是给编译器进行代码处理时的提示)。事实上,我们在日常开发中应该避免一些陷入复杂情况的方式:比如,传入参数都用引用(borrow),传出结果都应该是owner。rust也为我们提供了处理各种情况的工具。所以,一般来说,我们应该在传入参数的时候用&dyn T,传出结果用Box<dyn T>。

    作者回复: 真不错👍

    2023-11-10归属地:北京
    2
    14
  • 鸠摩智
    &dyn TraitA没有所有权,而Box<dyn TraitA>有所有权。

    作者回复: 对的

    2023-11-10归属地:江苏
    4
  • 哄哄
    关联类型之所以要单独设计,因为编译器可以通过输入判断具体类型,而无法推断出输出类型,所以,输出的类型需要明确指定

    作者回复: 思考方式很棒!

    2023-11-10归属地:北京
    2
    4
  • -
    最后安全的trait object听的似懂非懂,为什么是安全的,为什么是不安全的?希望具体讲下原因

    作者回复: 这里面的设计原理确实比较dirty了。我先给出三篇文章参考,你可以深入下去研究:https://rustcc.cn/article?id=e80a98d7-4adb-4d39-bf0a-4e079bcb1835 https://segmentfault.com/a/1190000022104780 https://rust-lang.github.io/rfcs/0255-object-safety.html

    2023-11-10归属地:北京
    4
    2
  • superggn
    笔记: trait 可以带泛型 generics trait 关联类型 associated type 跟泛型类似, 但必须在 impl 的时候就具化 泛型就随便啥时候具化都行了 `fn doit(i: u32) -> impl TraitA` => 某一种实现了 TraitA 的类型 `fn doit(i: u32) -> Box<dyn TraitA>` => 多种实现了 TraitA 的类型 前两次刷 rbe 和 rustbook 的时候都没反应过来为啥叫 trait object, 看这课终于明白了, 原来 trait obejct 指的是 typeOuter 的 OuterSizedObj, 然后这个 OuterSizedObj 里面可以包裹各种实现了 traitA 的 type 对应 obj 还是得多刷点儿资料, 多看对于同一概念的不同表达, 明白的快

    作者回复: 真棒!

    2023-12-15归属地:北京
    1
  • superggn
    思考题: &dyn traitA 和 Box<dyn traitA> 严格来说都是指向 actual object 的指针, 只不过 `&` 叫做引用, `Box` 叫做智能指针, 这俩都是固定大小的, 所以都能用在 trait object 区别: &dyn traitA 是一个不拿所有权的指针 => 所以经常用在参数里 Box<dyn traitA> 是拥有内部数据所有权的指针 => 所以经常用在返回值里, 参数里应该也能用

    作者回复: 对的。👍

    2023-12-15归属地:北京
    1
  • 李诗涛
    老师老师,我有一个问题。就是您说impl trait目前有两个使用的地方,分别是函数入参和返回。但给出的例子里,在入参使用impl trait时编译器会自动展开。但在出返回值使用impl trait时,若是返回了不同的类型却会报错。这是怎么理解?

    作者回复: 你可以再仔细理解一下impl trait 放在入参位置的示例和放在返回值位置的区别。它们实际都是编译期展开的应用,但是逻辑会有区别。由于返回值的if是运行时才有值来判断,因此在每次调用实例时,它无法在编译期推导出准确的返回值类型。

    2024-02-07归属地:上海
  • 小可爱(`へ´*)ノ
    基础部分非常详细,建议老师可以深入讲一下trait object的内存相关知识,以及trait object会丢失本身类型信息这些注意点。

    作者回复: 好滴, 记下了,后面会持续输出。

    2023-12-28归属地:四川
  • -Hedon🍭
    思考题:&dyn TraitA 是借用,Box<dyn TraitA> 会转移所有权。 通过下面的程序可以测试出来: fn doit3(t1: &dyn TraitA, t2: Box<dyn TraitA>) { println!("{:?}", t1); println!("{:?}", t2) } fn main() { let a = AType; let b = BType; doit3(&a, Box::new(b)); println!("{:?}", a); println!("{:?}", b); } 输出: error[E0382]: borrow of moved value: `b` --> examples/trait_object.rs:29:22 | 26 | let b = BType; | - move occurs because `b` has type `BType`, which does not implement the `Copy` trait 27 | doit3(&a, Box::new(b)); | - value moved here 28 | println!("{:?}", a); 29 | println!("{:?}", b); | ^ value borrowed here after move

    作者回复: 👍👍

    2023-11-21归属地:湖北
  • 老大
    不要在 trait 里面定义构造函数,比如 new 这种返回 Self 的关联函数。你可以发现,确实在整个 Rust 生态中都没有将构造函数定义在 trait 中的习惯。 这句话,在上面的例子中 确实有在trait中定义了new函数返回self的。怎么就感觉有冲突呢?

    作者回复: 观察得很细致,上面的例子主要是为了展现可能出现的写法。后面的说明加入了object safty的因素,以及社区的约定,所以看起来好像不一致,实际是一致的。

    2023-11-21归属地:河南
收起评论
显示
设置
留言
15
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部