Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use `impl Trait` as a function's return type in a trait definition?

Tags:

rust

traits

Is it at all possible to define functions inside of traits as having impl Trait return types? I want to create a trait that can be implemented by multiple structs so that the new() functions of all of them returns an object that they can all be used in the same way without having to write code specific to each one.

trait A {
    fn new() -> impl A;
}

However, I get the following error:

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/lib.rs:2:17
  |
2 |     fn new() -> impl A;
  |                 ^^^^^^

Is this a limitation of the current implementation of impl Trait or am I using it wrong?

like image 693
Ameo Avatar asked Sep 14 '16 03:09

Ameo


People also ask

Can Rust traits be applied to any type?

As Rust by Example puts it: A trait is a collection of methods defined for an unknown type: Self . They can access other methods declared in the same trait. When we want to define a function that can be applied to any type with some required behavior, we use traits.

What is impl in Rust?

The impl keyword is primarily used to define implementations on types. Inherent implementations are standalone, while trait implementations are used to implement traits for types, or other traits. Functions and consts can both be defined in an implementation.

What are Rust traits?

A trait in Rust is a group of methods that are defined for a particular type. Traits are an abstract definition of shared behavior amongst different types. So, in a way, traits are to Rust what interfaces are to Java or abstract classes are to C++. A trait method is able to access other methods within that trait.

What is dyn Rust?

dyn is a prefix of a trait object's type. The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed.


3 Answers

As trentcl mentions, you cannot currently place impl Trait in the return position of a trait method.

From RFC 1522:

impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.

  • Eventually, we will want to allow the feature to be used within traits [...]

For now, you must use a boxed trait object:

trait A {     fn new() -> Box<dyn A>; } 

See also:

  • Is it possible to have a constructor function in a trait?
  • Why can a trait not construct itself?
  • How do I return an instance of a trait from a method?

Nightly only

If you wish to use unstable nightly features, you can use existential types (RFC 2071):

// 1.40.0-nightly (2019-11-05 1423bec54cf2db283b61) #![feature(type_alias_impl_trait)]  trait FromTheFuture {     type Iter: Iterator<Item = u8>;      fn example(&self) -> Self::Iter; }  impl FromTheFuture for u8 {     type Iter = impl 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);     } } 
like image 125
Shepmaster Avatar answered Sep 19 '22 05:09

Shepmaster


You can get something similar even in the case where it's not returning Self by using an associated type and explicitly naming the return type:

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     } } 
like image 32
Jeremy Salwen Avatar answered Sep 20 '22 05:09

Jeremy Salwen


If you only need to return the specific type for which the trait is currently being implemented, you may be looking for Self.

trait A {
    fn new() -> Self;
}

For example, this will compile:

trait A {
    fn new() -> Self;
}

struct Person;

impl A for Person {
    fn new() -> Person {
        Person
    }
}

Or, a fuller example, demonstrating using the trait:

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!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}

fn get_name<T: A>(a: &T) -> String {
    a.get_name()
}

Playground

As a side note.. I have used String here in favor of &str references.. to reduce the need for explicit lifetimes and potentially a loss of focus on the question at hand. I believe it's generally the convention to return a &str reference when borrowing the content and that seems appropriate here.. however I didn't want to distract from the actual example too much.

like image 34
Simon Whitehead Avatar answered Sep 20 '22 05:09

Simon Whitehead