Why can't `Self` be used to refer to an enum&#

2019-01-19 11:53发布

问题:

The following Rust code fails to compile:

enum Foo {
    Bar,
}

impl Foo {
    fn f() -> Self {
        Self::Bar
    }
}

The error message confuses me:

error[E0599]: no associated item named `Bar` found for type `Foo` in the current scope
 --> src/main.rs:7:9
  |
7 |         Self::Bar
  |         ^^^^^^^^^

The problem can be fixed by using Foo instead of Self, but this strikes me as strange since Self is supposed to refer to the type that is being implemented (ignoring traits), which in this case is Foo.

enum Foo {
    Bar,
}

impl Foo {
    fn f() -> Self {
        Foo::Bar
    }
}

Why can't Self be used in this situation? Where exactly can Self be used*? Is there anything else I can use to avoid repeating the type name in the method body?

* I'm ignoring usage in traits, where Self refers to whatever type implements the trait.

回答1:

An important thing to note is that the error said associated item. enum Foo { Baz } doesn't have associated items. A trait can have an associated item:

trait FooBaz { type Baz }
//             ^~~~~~~~ - associated item

To summarize:

Why can't Self be used in this situation?

Because of this issue. RFC 2338 has not been implemented yet.

Self seems to act as a type alias, albeit with some modifications.

Where exactly can Self be used?

Self can only be used in traits and impls. This code:

struct X {
    f: i32,
    x: &Self,
}

Outputs the following:

error[E0411]: cannot find type `Self` in this scope
 --> src/main.rs:3:9
  |
3 |     x: &Self,
  |         ^^^^ `Self` is only available in traits and impls

This is possibly a temporary situation and might change in the future!

More precisely, Self should be used only as part of method signature (e.g. fn self_in_self_out(&self) -> Self) or to access an associated type:

enum Foo {
    Baz,
}

trait FooBaz {
    type Baz;

    fn b(&self) -> Self::Baz; // Valid use of `Self` as method argument and method output
}


impl FooBaz for Foo {
    type Baz = Foo;

    fn b(&self) -> Self::Baz {
        let x = Foo::Baz as Self::Baz; // You can use associated type, but it's just a type
        x
    }
}

I think user4815162342 covered the rest of the answer best.



回答2:

If the enum name Foo is in reality long and you want to avoid repeating it across the implementation, you have two options:

  • use LongEnumName as Short at module level. This will allow you to return Short::Bar at the end of f.
  • use LongEnumName::* at module level, allowing an even shorter Bar.

If you omit pub, the imports will be internal and won't affect the public API of the module.



回答3:

Enum constructors != associated items.

It is a known issue, but it's not expected to be fixed, at least not in the foreseeable future. From what I have gathered it is not trivial to just allow this to work; at this point it is more likely that the related documentation or the error message will be improved.

There is little documentation I could find on the topic of associated items in general; The Rust Book has a chapter on associated types, though. In addition, there are plenty of good answers about Self in this related question.



标签: enums rust