[英]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.