Использование определения "Признак в определении"

Можно ли вообще определить функции внутри признаков как имеющие 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;
}

но я изо всех сил пытаюсь обвести голову этим последним.