![](/img/trans.png)
[英]Understanding "the trait X cannot be made into an object" for `&mut Box<Self>` parameter
[英]Understanding the 'self' parameter in the context of trait implementations
在实现特征时,我们经常使用关键字self
,示例如下。 我想了解此代码示例中self
的许多用法的表示。
struct Circle {
x: f64,
y: f64,
radius: f64,
}
trait HasArea {
fn area(&self) -> f64; // first self: &self is equivalent to &HasArea
}
impl HasArea for Circle {
fn area(&self) -> f64 { //second self: &self is equivalent to &Circle
std::f64::consts::PI * (self.radius * self.radius) // third:self
}
}
我的理解是:
self
: &self
等同于&HasArea
。 self
: &self
等同于&Circle
。 self
代表Circle
吗? 如果是这样,如果self.radius
被使用了两次,那会导致移动问题吗? 此外,将非常感谢更多示例以显示在不同上下文中self
关键字的不同用法。
你大部分都是对的。
我想到的方式是在方法签名中, self
是一种简写:
impl S {
fn foo(self) {} // equivalent to fn foo(self: S)
fn foo(&self) {} // equivalent to fn foo(self: &S)
fn foo(&mut self) {} // equivalent to fn foo(self: &mut S)
}
它实际上并不等同,因为self
是一个关键字,并且有一些特殊的规则(例如终身省略),但它非常接近。
回到你的例子:
impl HasArea for Circle {
fn area(&self) -> f64 { // like fn area(self: &Circle) -> ...
std::f64::consts::PI * (self.radius * self.radius)
}
}
身体中的self
是类型&Circle
。 你不能移出引用,所以self.radius
甚至不能移动一次。 在这种情况下, radius
实现了Copy
,因此它只是被复制而不是移动。 如果它是一个更复杂的类型,没有实现Copy
那么这将是一个错误。
你大多是正确的。
有一个巧妙的技巧让编译器告诉你变量的类型而不是试图推断它们: let () = ...;
。
使用游乐场我得到的第一个案例:
9 | let () = self;
| ^^ expected &Self, found ()
对于第二种情况:
16 | let () = self;
| ^^ expected &Circle, found ()
第一种情况实际上是特殊的,因为HasArea
不是一种类型,它是一种特性。
那么self
是什么? 这是什么呢 。
换句话说,它构成了可能实现HasArea
任何可能的具体类型 。 因此,我们对此特性的唯一保证是它至少提供了HasArea
的界面。
关键是你可以放置额外的边界。 例如你可以说:
trait HasArea: Debug {
fn area(&self) -> f64;
}
在这种情况下, Self: HasArea + Debug
,这意味着self
提供了两个接口HasArea
和Debug
。
第二和第三种情况要容易得多:我们知道实施HasArea
特征的确切具体类型 。 这是Circle
。
因此, fn area(&self)
方法中的self
类型是&Circle
。
请注意,如果参数的类型是&Circle
那么它在方法的所有用途中都是&Circle
。 Rust具有静态类型(并且没有依赖于流的类型),因此给定绑定的类型在其生命周期内不会更改。
然而事情会变得更复杂。
想象一下,你有两个特点:
struct Segment(Point, Point);
impl Segment {
fn length(&self) -> f64;
}
trait Segmentify {
fn segmentify(&self) -> Vec<Segment>;
}
trait HasPerimeter {
fn has_perimeter(&self) -> f64;
}
然后,您可以自动为所有可在细分序列中细分的形状实施HasPerimeter
。
impl<T> HasPerimeter for T
where T: Segmentify
{
// Note: there is a "functional" implementation if you prefer
fn has_perimeter(&self) -> f64 {
let mut total = 0.0;
for s in self.segmentify() { total += s.length(); }
total
}
}
这里的self
类型是什么? 这是&T
。
什么是T
? 任何实现Segmentify
类型。
因此,我们只知道T
的是,它实现了Segmentify
和HasPerimeter
,没有别的(我们不能使用println("{:?}", self);
因为T
不保证实现Debug
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.