繁体   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