不支持OOP的Rust怎么实现面向对象?
面向对象的程序由对象组成。一个对象包含数据和操作这些数据的过程。这些过程通常被称为方法 或操作。
封装(encapsulation):一个对象的实现细节对使用该对象的代码不可见。
继承(Inheritance)是一种机制:一个对象可以从另一个对象的定义中继承元素,从而获得父对象的数据和行为。Rust原生不支持继承,但是Rust可以使用宏实现集成。
多态(polymorphism):如果多个对象共享某些特征,可以在运行时将它们互相替代。
bounded parametric polymorphism:Rust 使用泛型来抽象不同可能的类型,并通过 trait bound 来约束这些类型所必须提供的内容。
鸭子类型(duck typing):如果它走起来像一只鸭子,叫起来像一只鸭子,那么它就是一只鸭子!
单态化处理:编译器为每一个被泛型类型参数代替的具体类型生成了函数和方法的非泛型实现。单态化产生的代码在执行静态分发(static dispatch),也就是说编译器在编译时就知晓要调用什么方法。
动态分发 (dynamic dispatch):编译器会生成负责在运行时确定该调用什么方法的代码。
trait bound在编译时单态化,trait对象只能动态分发。
模式由如下一些内容组合而成:
字面值
已解构的数组、枚举、结构体或者元组
变量
通配符
占位符
match和if let可以互换使用,区别在于if let并不要求穷尽,编译器也不会警告未处理的分支。
C的赋值就是赋值,Rust的赋值是模式匹配。现代编程语言大多是模式匹配赋值。
模式有两种形式:refutable(可反驳的)和 irrefutable(不可反驳的)。
能匹配任何传递的可能值的模式被称为是不可反驳的(irrefutable)。一个例子就是 let x = 5; 语句中的 x,因为 x 可以匹配任何值所以不可能会失败。
对某些可能的值进行匹配会失败的模式被称为是可反驳的(refutable)。一个这样的例子便是 if let Some(x) = a_value 表达式中的 Some(x);如果变量 a_value 中的值是 None 而不是 Some,那么 Some(x) 模式不能匹配。
refutable用于赋值、参数传递、for循环,irrefutable用于条件表达式。1.65以后可以用 let Some(x) = some_option_value else { return; }; 写法。
at 运算符(@)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式。
match msg {
Message::Hello { id: u8 @ 3..=7 } => todo!(),
...
}
范围约束在Rust match常用,在其他语言不常用。标准C的switch case不可能case一个范围,golang的可以是范围但需要用条件表达式写法。Gcc给C扩展了范围写法,但不是标准,不能跨平台兼容。
Unsafe就是告诉编译器“相信我,这段代码是安全的”。
不安全的超能力(unsafe superpowers)(除了下述5种能力,在不安全的代码块中,Rust仍然进行其他安全检查,例如引用)
引用裸指针(裸指针就是C里面的指针,一个指向任意内存地址或者为空的值)
调用不安全的函数或方法(FFI)
访问或修改可变静态变量
实现不安全 trait
访问 union 的字段
可以同时创建同一地址的可变和不可变裸指针,但不能同时创建可变和不可变引用。
调用unsafe的结果返回值是safe的。
全局可变静态变量的修改总是不安全的,如果一定要修改,需要unsafe,或者使用线程安全的智能指针。
trait可以是unsafe的,如果我们定义的类型包含某些未实现 Send 或 Sync 的类型,例如裸指针,但又想将该类型标记为 Send 或 Sync,就必须使用 unsafe。
如果要在新模块中为其他模块类型实现trait,可以使用newtype模式绕过孤儿规则。
闭包不能作为fn返回,但是可以作为Fn trait返回。