Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I return an instance of a trait from a method?

Tags:

rust

traits

I'm trying to create a function that returns an instance of the Shader trait. Here is my drastically simplified code:

trait Shader {}  struct MyShader; impl Shader for MyShader {}  struct GraphicsContext;  impl GraphicsContext {     fn create_shader(&self) -> Shader {         let shader = MyShader;         shader     } }  fn main() {} 

However I receive the following error:

error[E0277]: the trait bound `Shader + 'static: std::marker::Sized` is not satisfied   --> src/main.rs:10:32    | 10 |     fn create_shader(&self) -> Shader {    |                                ^^^^^^ `Shader + 'static` does not have a constant size known at compile-time    |    = help: the trait `std::marker::Sized` is not implemented for `Shader + 'static`    = note: the return type of a function must have a statically known size 

Newer versions of the compiler have this error:

error[E0277]: the size for values of type `(dyn Shader + 'static)` cannot be known at compilation time  --> src/main.rs:9:32   | 9 |     fn create_shader(&self) -> Shader {   |                                ^^^^^^ doesn't have a size known at compile-time   |   = help: the trait `std::marker::Sized` is not implemented for `(dyn Shader + 'static)`   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>   = note: the return type of a function must have a statically known size 

This makes sense as the compiler doesn't know the size of the trait, but nowhere can I find the recommended way of fixing this. Passing back a reference with & wouldn't work as far as I know because the reference would outlive the lifetime of its creator.

Perhaps I need to use Box<T>?

like image 280
neon64 Avatar asked Jun 05 '15 07:06

neon64


People also ask

What does Dyn do in 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.

What is box DYN in Rust?

The dyn keyword is used when declaring a trait object: The size of a trait is not known at compile-time; therefore, traits have to be wrapped inside a Box when creating a vector trait object.

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 is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.


2 Answers

Rust 1.26 and up

impl Trait now exists:

fn create_shader(&self) -> impl Shader {     let shader = MyShader;     shader } 

It does have limitations, such as not being able to be used in a trait method and it cannot be used when the concrete return type is conditional. In those cases, you need to use the trait object answer below.

Rust 1.0 and up

You need to return a trait object of some kind, such as &T or Box<T>, and you're right that &T is impossible in this instance:

fn create_shader(&self) -> Box<Shader> {     let shader = MyShader;     Box::new(shader) } 

See also:

  • What is the correct way to return an Iterator (or any other trait)?
  • Conditionally iterate over one of several possible iterators
like image 150
Steve Klabnik Avatar answered Sep 23 '22 02:09

Steve Klabnik


I think this is what you were searching for; a simple factory implemented in Rust:

pub trait Command {     fn execute(&self) -> String; }  struct AddCmd; struct DeleteCmd;  impl Command for AddCmd {     fn execute(&self) -> String {         "It add".into()     } }  impl Command for DeleteCmd {     fn execute(&self) -> String {         "It delete".into()     } }  fn command(s: &str) -> Option<Box<Command + 'static>> {     match s {         "add" => Some(Box::new(AddCmd)),         "delete" => Some(Box::new(DeleteCmd)),         _ => None,     } }  fn main() {     let a = command("add").unwrap();     let d = command("delete").unwrap();     println!("{}", a.execute());     println!("{}", d.execute()); } 
like image 33
Cristian Oliveira Avatar answered Sep 25 '22 02:09

Cristian Oliveira