自我和Self有什么区别?

135

我在文档中没找到 Self,只在源代码中看到了它。而文档中只使用 self


1
请在此处查看文档:https://doc.rust-lang.org/reference.html#self-types - phoenix
6
这份文档已迁移至:https://doc.rust-lang.org/reference/types.html#self-types。 - youngmit
3个回答

197

Self 是当前对象的类型。它可能会出现在 trait 或者 impl 中,但通常出现在 trait 中,在这里它是一个替身,表示最终将要实现该 trait 的任何类型(在定义 trait 时未知):

trait Clone {
    fn clone(&self) -> Self;
}

如果我实现Clone

impl Clone for MyType {
    // I can use either the concrete type (known here)
    fn clone(&self) -> MyType;

    // Or I can use Self again, it's shorter after all!
    fn clone(&self) -> Self;
}

如果我懒的话,我也可以在常规的impl中使用它(它更短!):

impl MySuperLongType {
    fn new(a: u32) -> Self { ... }
}

self是在traitimpl中用于方法的第一个参数的名称。使用其他名称也是可能的,但有一个显著的区别:

  • 如果使用self,则引入的函数是一个方法
  • 如果使用任何其他名称,则引入的函数是一个关联函数

在Rust中,没有隐式的this参数传递给类型的方法:您必须显式地将“当前对象”作为方法参数传递。这将导致:

impl MyType {
    fn doit(this: &MyType, a: u32) { ... }
}

正如我们所看到的,作为一种更短的形式,这也可以是(仍然啰嗦):

impl MyType {
    fn doit(this: &Self, a: u32) { ... }
}

实际上这就是在底层发生的&self的本质。

impl MyType {
    fn doit(&self, a: u32) { ... }
}

因此,对应表如下:

self => self: Self
&self => self: &Self
&mut self => self: &mut Self

但是,调用这些函数的方式发生了变化:

impl MyType {
    fn doit(&self, a: u32) {
        // ...
    }
    fn another(this: &Self, a: u32) {
        // ...
    }
}

fn main() {
    let m = MyType;

    // Both can be used as an associated function
    MyType::doit(&m, 1);
    MyType::another(&m, 2);

    // But only `doit` can be used in method position
    m.doit(3);     // OK: `m` is automatically borrowed
    m.another(4);  // ERROR: no method named `another`
}

1
它可以出现在特质或实现中,难道不能出现在“结构体”中吗? - user266003
@jawanam:如果我尝试这样做,我个人会收到“在impl或trait之外使用Self”的错误。也许在更近期的版本中限制已经解除了? - Matthieu M.
1
不,目前还不可能(截至2015年9月1日的夜间版本)。自引用结构非常罕见,因此在这种情况下添加自处理被认为是低优先级的。 - llogiq
@Sergey.quixoticaxis.Ivanov:使用self与否不仅仅是语法问题,因为这是方法和关联函数之间的区别。对于一个简单的struct,调用方法或关联函数在语法上大体相同(虽然糖并不那么糟糕)。然而,在特性上情况就有所改变:具体来说,Trait对象(例如与fn doit(d: &Display)一起使用)只能在对象安全的特性上形成,而关联函数不是对象安全的(方法的子集也不安全)。 - Matthieu M.

114

self被用作第一个方法参数时,它是self: Self的简写。还有&self,相当于self: &Self,以及&mut self,相当于self: &mut Self

在方法参数中使用的Self是接收方法的类型(即该方法所在的impl)的一种语法糖。这也允许使用不需要太多重复的泛型类型。


2
与Java相比,我能把Self和self看作是类(class)和当前对象(this)吗? - Ashwin Rohit

16

Self指的是实现trait的当前类型,而self则指代实例。

在Rust中,将self作为第一个参数定义方法。这只是一种惯例,将函数转换为方法,类似于Python。从功能上讲,self类似于JavaScript中的this

对于那些不知道函数和方法区别的人来说,方法是附加到实例并通过该实例调用的函数。

Self是一个通用类型,这就是为什么它不允许出现在需要具体类型的任何位置的原因。这在Rust文档中通常被称为对象安全

Self还在impl块内的方法定义中使用,以便在重构期间重命名类型时,您不必逐个修复每个方法。

在Rust中,self还用于模块解析,它指的是当前模块。在此示例中,它导入io模块:

use std::io::{self, Read};

1
这个答案为什么不正确呢?假设它是准确的,我更喜欢它,因为它简洁明了,对我来说更容易理解和记忆。 - Johnny Utahh
@JohnnyUtahh 这并不是错误的,只是还没有被投票。 - snnsnn
2
明白了。回答得很好,编辑也很有帮助——没有失去第一句话的简洁性,这很棒。我已经点赞了。 - Johnny Utahh
我认为这个答案更加更新,应该被接受作为回复。它也更加通用。 - PrisionMike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接