簡體   English   中英

理解特征實現上下文中的“self”參數

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

我的理解是:

  1. 第一個self&self等同於&HasArea
  2. 第二個self&self等同於&Circle
  3. 第三個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提供了兩個接口HasAreaDebug


第二和第三種情況要容易得多:我們知道實施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的是,它實現了SegmentifyHasPerimeter ,沒有別的(我們不能使用println("{:?}", self);因為T不保證實現Debug )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM