问题历史
Rust 从面向对象语言中借鉴了方法的概念,但实现方式不同:方法显式接受 self 参数,而不是常见的 this 或 self。关联函数则作为其他编程语言中静态方法的替代存在——它们与类型相关,但与具体值无关。
问题
人们常常混淆方法、关联函数和自由函数。不明确何时使用带 self 的方法,何时使用不带 self 的关联函数。还有可见性、自动去引用、所有权传递等问题。
解决方案
在 Rust 中,方法是在 impl 块内部声明的,首个参数为 self/ &self/ &mut self(通常用于结构体或枚举)。它们通过实例调用: object.method()。关联函数(例如,new、from)也在 impl 内部声明,但没有首个 self 参数,通过双冒号调用: Type::function()。
代码示例:
struct Point { x: f64, y: f64, } impl Point { // 关联函数(构造函数) fn new(x: f64, y: f64) -> Self { Self { x, y } } // 方法:需要 self fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0
关键特性:
:: 调用可以通过实例调用关联函数(通过点操作符)吗?
是的(例如,p.new(1.0, 2.0)),但极不推荐:这会造成误解,因为关联函数无法访问当前对象,而实例被忽略地传递。最好使用 Type::func() 语法。
代码示例:
let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // 可以工作,但不是最佳实践!
方法可以是异步的吗?
可以。方法可以使用 async 关键字声明,和自由函数一样:
impl Foo { async fn do_async(&self) { // ... } }
可以在同一个 impl 块中声明方法和关联函数吗?
可以——任何组合都是允许的。对于一个类型也可以声明多个 impl 块。
新手在 impl 外声明了 new 函数,并试图将其用作构造函数,后来无意中通过实例调用了: p.new(1.0, 2.0)。
优点:
工作迅速(编译器允许)。
缺点:
代码可读性差,难以维护,使用 self 的正确所有权方法困难。
所有方法和关联函数严格声明在 impl 内部,使用正确的调用语法(Type::new() 用于构造函数,obj.method() 用于操作)。
优点:
高可读性,符合最佳实践。
缺点:
需要了解 Rust 的习惯用法并对语法保持关注。