Использование определения "Признак в определении"
Можно ли вообще определить функции внутри признаков как имеющие impl Trait
возвращаемые типы? Я хочу создать признак, который может быть реализован несколькими структурами, чтобы функции new()
всех из них возвращали объект, к которому все они могут быть использованы одинаково, без необходимости писать код, специфичный для каждого из них.
#![feature(conservative_impl_trait)]
trait A {
fn new() -> impl A;
}
Однако я получаю следующую ошибку:
impl Trait
не допускается вне функций и свойств возвращаемого метода
Является ли это просто ограничением текущей реализации impl Trait
в Rust или я использую ее неправильно?
Ответы
Ответ 1
Если вам нужно только вернуть конкретный тип, для которого черта реализуется в данный момент, возможно, вы ищете Self
.
trait A {
fn new() -> Self;
}
Например, это скомпилирует:
trait A {
fn new() -> Self;
}
struct Person;
impl A for Person {
fn new() -> Person {
Person
}
}
Или более полный пример, демонстрирующий использование черты:
trait A {
fn new<S: Into<String>>(name: S) -> Self;
fn get_name(&self) -> String;
}
struct Person {
name: String
}
impl A for Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
struct Pet {
name: String
}
impl A for Pet {
fn new<S: Into<String>>(name: S) -> Pet {
Pet { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
fn main() {
let person = Person::new("Simon");
let pet = Pet::new("Buddy");
println!("{} pets name is {}", get_name(&person), get_name(&pet));
}
fn get_name<T: A>(a: &T) -> String {
a.get_name()
}
площадка
В качестве примечания... Я использовал здесь String
в пользу ссылок &str
... чтобы уменьшить потребность в явном времени жизни и, возможно, потерю фокуса на рассматриваемом вопросе. Я считаю, что обычно принято возвращать ссылку &str
при заимствовании контента, и это кажется уместным здесь... однако я не хотел слишком сильно отвлекаться от фактического примера.
Ответ 2
Как trentcl упоминает, в настоящее время вы не можете поместить impl Trait
в позицию возврата метода черты.
С RFC 1522:
impl Trait
может быть записан только в возвращаемом типе отдельно стоящей или врожденной функции impl, но не в определениях признаков или какой-либо позиции невозвращаемого типа. Они также могут отсутствовать в возвращаемых типах закрывающих признаков или указателей на функции, если только они сами не являются частью легального возвращаемого типа.
- В конце концов, мы захотим разрешить использование этой функции в чертах [...]
На данный момент вы должны использовать объект в штучной упаковке:
trait A {
fn new() -> Box<dyn A>;
}
Смотрите также:
Только по ночам
Если вы хотите использовать нестабильные ночные функции, вы можете использовать экзистенциальные типы (RFC 2071):
#![feature(existential_type)]
trait FromTheFuture {
type Iter: Iterator<Item = u8>;
fn example(&self) -> Self::Iter;
}
impl FromTheFuture for u8 {
existential type Iter: Iterator<Item = u8>;
fn example(&self) -> Self::Iter {
std::iter::repeat(*self).take(*self as usize)
}
}
fn main() {
for v in 7.example() {
println!("{}", v);
}
}
Ответ 3
Вы можете получить что-то подобное даже в том случае, если он не возвращает "Я", явно называя возвращаемый тип.
trait B {}
struct C;
impl B for C {}
trait A {
type FReturn: B;
fn f() -> Self::FReturn;
}
struct Person;
impl A for Person {
type FReturn = C;
fn f() -> C {
C
}
}
Ответ 4
Довольно плохо знаком с Rust, поэтому может потребоваться проверка.
Вы можете параметризовать тип возвращаемого значения. Это имеет ограничения, но они менее строгие, чем просто возврат Self
.
trait A<T> where T: A<T> {
fn new() -> T;
}
// return a Self type
struct St1;
impl A<St1> for St1 {
fn new() -> St1 { St1 }
}
// return a different type
struct St2;
impl A<St1> for St2 {
fn new() -> St1 { St1 }
}
// won't compile as u32 does not implement A<u32>
struct St3;
impl A<u32> for St3 {
fn new() -> u32 { 0 }
}
В этом случае ограничением является то, что вы можете возвращать только тип T
, который реализует A<T>
. Здесь St1
реализует A<St1>
, так что все в порядке для St2
- impl A<St2>
. Однако он не будет работать, например, с
impl A<St1> for St2 ...
impl A<St2> for St1 ...
Для этого вам нужно еще больше ограничить типы, например
trait A<T, U> where U: A<T, U>, T: A<U, T> {
fn new() -> T;
}
но я изо всех сил пытаюсь обвести голову этим последним.