Почему "Self" нельзя использовать для обозначения варианта перечисления в теле метода?
Этот вопрос сейчас устарел, потому что эта функция была реализована. Связанный ответ.
Следующий код Rust не компилируется:
enum Foo {
Bar,
}
impl Foo {
fn f() -> Self {
Self::Bar
}
}
Сообщение об ошибке смущает меня:
error[E0599]: no associated item named 'Bar' found for type 'Foo' in the current scope
--> src/main.rs:7:9
|
7 | Self::Bar
| ^^^^^^^^^
Проблема может быть исправлена с помощью Foo
вместо Self
, но это кажется мне странным, поскольку Self
должен ссылаться на реализуемый тип (игнорируя черты), который в этом случае Foo
.
enum Foo {
Bar,
}
impl Foo {
fn f() -> Self {
Foo::Bar
}
}
Почему нельзя использовать Self
в этой ситуации? Где именно можно использовать Self
*? Можно ли использовать что-либо еще, чтобы избежать повторения имени типа в теле метода?
* I'm ignoring usage in traits, where [TG49] refers to whatever type implements the trait.
Ответы
Ответ 1
Важно отметить, что в сообщении об ошибке упоминается связанный элемент. enum Foo { Baz }
не имеет связанных предметов. Черта может иметь связанный элемент:
trait FooBaz { type Baz }
// ^~~~~~~~ - associated item
Подвести итоги:
Почему нельзя использовать Self
в этой ситуации?
Из-за этой проблемы. RFC 2338 еще не был реализован.
Кажется, что Self
действует как псевдоним типа, хотя и с некоторыми изменениями.
Где именно можно использовать Self
?
Самость может быть использована только в чертах и impl
. Этот код:
struct X {
f: i32,
x: &Self,
}
Выводит следующее:
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
Возможно, это временная ситуация и может измениться в будущем!
Точнее, Self
следует использовать только как часть сигнатуры метода (например, fn self_in_self_out(&self) → Self
) или для доступа к связанному типу:
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 just a type
x
}
}
Я думаю, что user4815162342 покрыл остальную часть ответа лучше всего.
Ответ 2
Если имя перечисления Foo
на самом деле длинное, и вы хотите избежать повторения его в реализации, у вас есть два варианта:
-
use LongEnumName as Short
на уровне модуля. Это позволит вам вернуть Short::Bar
в конце f
.
-
use LongEnumName::*
на уровне модуля, что позволяет еще короче Bar
.
Если вы опустите pub
, импорт будет внутренним и не повлияет на открытый API модуля.
Ответ 3
Конструкторы Enum!= связанные элементы.
Это известный issue, но он не ожидается, что он будет исправлен, по крайней мере, в обозримом будущем. Из того, что я собрал, нетрудно просто позволить этому работать; на данный момент более вероятно, что соответствующая документация или сообщение об ошибке будут улучшены.
Существует небольшая документация, которую я мог бы найти по теме связанных элементов в целом; Однако в книге Rust Book есть глава о связанных типах. Кроме того, есть много хороших ответов о Self
в этом связанном вопросе.
Ответ 4
Существует экспериментальная функция, которая позволит вашему примеру работать без каких-либо других изменений. Вы можете попробовать его в ночной сборке Rust, добавив это в свой основной файл:
#![feature(type_alias_enum_variants)]
Вы можете следить за ходом функции к стабилизации в ее проблеме отслеживания.
Ответ 5
Теперь это возможно начиная с версии 1.37.