25|类型系统:如何围绕trait来设计和架构系统?
陈天
该思维导图由 AI 生成,仅供参考
你好,我是陈天。
Trait,trait,trait,怎么又是 trait?how old are you?
希望你还没有厌倦我们没完没了地聊关于 trait 的话题。因为 trait 在 Rust 开发中的地位,怎么吹都不为过。
其实不光是 Rust 中的 trait,任何一门语言,和接口处理相关的概念,都是那门语言在使用过程中最重要的概念。软件开发的整个行为,基本上可以说是不断创建和迭代接口,然后在这些接口上进行实现的过程。
在这个过程中,有些接口是标准化的,雷打不动,就像钢筋、砖瓦、螺丝、钉子、插座等这些材料一样,无论要构筑的房子是什么样子的,这些标准组件的接口在确定下来后,都不会改变,它们就像 Rust 语言标准库中的标准 trait 一样。
而有些接口是跟构造的房子息息相关的,比如门窗、电器、家具等,它们就像你要设计的系统中的 trait 一样,可以把系统的各个部分联结起来,最终呈现给用户一个完整的使用体验。
之前讲了 trait 的基础知识,也介绍了如何在实战中使用 trait 和 trait object。今天,我们再花一讲的时间,来看看如何围绕着 trait 来设计和架构系统。
由于在讲架构和设计时,不免要引入需求,然后我需要解释这需求的来龙去脉,再提供设计思路,再介绍 trait 在其中的作用,但这样下来,一堂课的内容能讲好一个系统设计就不错了。所以我们换个方式,把之前设计过的系统捋一下,重温它们的 trait 设计,看看其中的思路以及取舍。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了如何使用trait来设计和架构系统,强调了良好的接口设计对系统的重要性。通过具体的代码示例和思考,阐述了如何围绕trait来设计和架构系统,以及如何通过trait让代码更加自然舒服好用。文章重点讨论了trait在Rust开发中的重要性,并指出任何一门语言中,接口处理相关的概念都是最重要的。此外,还介绍了如何使用trait进行控制反转,以及如何通过trait提供桥接功能。通过trait的灵活性,可以让调用者和被调用者只需要关心它们之间的接口,而非具体的数据结构。作者还强调了面向对象设计时SOLID原则中的重要性,以及如何在Rust中践行这些原则。总的来说,本文通过具体的代码示例和思考,深入浅出地介绍了如何围绕trait来设计和架构系统,强调了良好的接口设计对系统的重要性。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《陈天 · Rust 编程第一课》,新⼈⾸单¥68
《陈天 · Rust 编程第一课》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(8)
- 最新
- 精选
- 郑晔老师对单一职责的理解是错的,这一点是我特意在《软件设计之美》中纠正的,因为单一功能是一个永远为真的说法,单一职责是要把变化纳入考量。具体的可以参考《软件设计之美》和《架构整洁之道》中关于单一职责的讲解。
作者回复: 谢谢指正。我的理解来自于 https://en.wikipedia.org/wiki/Single-responsibility_principle > 一个类或模块应该有一个,而且只有一个,改变(例如重写)的理由
2021-10-3035 - Marvichovdoc: https://github.com/yewstack/yew/blob/master/packages/yew/src/html/component/mod.rs#L42 1. 不能, 返回类型有Self; 运行时type被erase掉了, 不知道Self具体是什么类型. 2. Message感觉有点像actor里面的message: op to change component state. 应该是用于定义交互的时候, dynamic change component的. Properties应该是DOM的property, 用于render view的… 比如impl Component for List https://github.com/yewstack/yew/blob/master/packages/yew/src/virtual_dom/vlist.rs#L495 ``` #[derive(Clone, Properties, PartialEq)] pub struct ListProps { pub children: Children, } impl Component for List { fn view(&self, ctx: &Context<Self>) -> Html { html! { <>{ for ctx.props().children.iter() }</> } } ``` 3. https://docs.rs/yew/0.18.0/yew/#example; 重点是`Sized` `+ 'Static` bound`. 实现的类型必须是Sized. 同时如果有reference element, 其lifetime必须是static的. 感觉virtual dom有必要了解了解...
作者回复: 👍 非常好!
2021-10-2321 - 核桃今天写了一段全局单例的代码,如下所示. ``` pub struct Monitor { sender: UnsafeCell<Option<mpsc::TxUnbounded<Vec<xxx>>>>, } impl Monitor { pub fn set_sender(&self, sender: mpsc::TxUnbounded<Vec<xxx>>) { let self_sender_option: &mut Option<mpsc::TxUnbounded<Vec<xxx>>> = unsafe { transmute(self.sender.get()) }; self_sender_option.replace(sender); } pub fn send_monitor(&self, info_vec: Vec<xxx>) { let self_sender: &mut Option<mpsc::TxUnbounded<Vec<xxx>>> = unsafe { transmute(self.sender.get()) }; ... } } unsafe impl Send for Monitor {} unsafe impl Sync for Monitor {} //why we use Arc to contain Monitor is that lazy_static seems a const value. //rust can't change the const value unless new at first. //but Arc can share to use the value. lazy_static! { pub static ref GLOBAL_MONITOR: Arc<Monitor> = Arc::new(Monitor { sender: UnsafeCell::new(None) }); } ``` 这里使用lazy_static进行全局实现静态变量,但是因为初始化的时候并没有办法指向一个其他的变量,因此这里考虑使用了Arc处理,但是这段代码其实还是有点问题的,因为并没有考虑到竞争并发的情况,但是这里似乎更加适合使用once cell。rust在这部分的实现,感觉还是有点欠缺的。
作者回复: 👍
2021-11-252 - 0@1老师,想问下关于trait的一些问题, 下面是个代码例子, struct MyStruct; trait MyTrait{ fn test(&self); } impl MyTrait for MyStruct{ fn test(&self) { println!("MyStruct") } } impl MyTrait for &MyStruct{ fn test(&self) { println!("&MyStruct") } } impl MyTrait for *const MyStruct{ fn test(&self) { println!("*const MyStruct") } } fn main() { let my_struct = MyStruct; my_struct.test(); &my_struct.test(); let my_struct_ptr = &my_struct as *const MyStruct; my_struct_ptr.test(); } 1. impl MyTrait for &MyStruct 和 impl MyTrait for MyStruct 这2个有什么区别,什么情况下适合 为引用实现trait, impl MyTrait for &MyStruct 2. impl MyTrait for *const MyStruct 这种方式,给裸指针实现trait, 有什么使用场景么
作者回复: trait 就是让对应的类型拥有对应的行为。所以你为 struct 实现 trait,这个 struct 类型就有了 trait 的行为;你对 struct 的引用实现 trait,struct 类型的引用就有了 trait 的行为;裸指针也是类似。当你持有一个 *const MyStruct 的数据时,你可以调用 trait 的方法。对裸指针来说,一般实现 trait 的意义不大,因为 self 此时可干的事情很少。
2021-10-23 - 罗杰接口的设计要注重用户体验,这太重要了。
作者回复: 👍
2021-10-22 - David.DuTrait 对T的一些约束,同时也提醒了T的实现者,要按照这个去做,Fn的函数功能,同时也约束了自身,我感觉一套系统定义完一些列的Trait+数据结构后,剩下的就是实现了。2023-11-06归属地:河南
- 进击的Lancelot思考题: Component trait 可以做 trait object 么? 做不了 trait object,因为其中的方法并不符合对象安全原则,要么返回 Self 对象,要么带有泛型参数 关联类型 Message 和 Properties 的作用是什么? Message 的作用是用来和 Component 进行交互,使其获得动态能力的。 properties 则表达了 Component 的属性,当 Component 的父组件被重新渲染时,子组件要么重新生成,要么从传递给 changed 方法中的上下文接受新的 properties 做为使用者,该如何用 Component trait?它的 lifecycle 是什么样子的? 为自定义类型实现 Component trait 需要指定关联类型 Message 和 Properties,并提供 create 和 view 两个方法的自定义实现即可。它的 lifecycle 是静态生命周期,贯穿程序的始终2022-09-16归属地:广东
- 渡鸦10086思考题: 1. Component trait 的 create 方法会返回 Self,所以不能做 trait Object 2. 关联类型 Message 用于让 Components 类型动态化及可交互化,Component 可以使用 enum 或 () 来声明。 Properties 就是 Component 的属性,当 Component 的父组件被重新渲染时时,它将被重新创建,或者在传递给被改变的 life cycle 方法的上下文中接收新的属性。 3. YEW的官方实例:https://docs.rs/yew/0.18.0/yew/#example。life cycle : `'static` 代表静态生命周期 4. 没有前端经验。。2022-01-17
收起评论