web-dev-qa-db-ja.com

メソッドから特性のインスタンスを返すにはどうすればよいですか?

Shaderトレイトのインスタンスを返す関数を作成しようとしています。ここに私の劇的に単純化されたコードがあります:

trait Shader {}

struct MyShader;
impl Shader for MyShader {}

struct GraphicsContext;

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

fn main() {}

ただし、次のエラーが表示されます。

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

新しいバージョンのコンパイラには、次のエラーがあります。

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

コンパイラーは特性のサイズを認識していないため、これは理にかなっていますが、これを修正するための推奨される方法はどこにもありません。 &を使用して参照を返すことは、参照がその作成者の存続期間よりも長く存続するため、私の知る限りでは機能しません。

おそらくBox<T>を使用する必要がありますか?

26
neon64

Rust 1.26以降

_impl Trait_が存在するようになりました

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

トレイトメソッドで使用できないなどの制限があり、具体的な戻り値の型が条件付きの場合は使用できません。そのような場合は、以下の特性オブジェクトの回答を使用する必要があります。

Rust 1.0以降

_&T_や_Box<T>_など、何らかのtraitオブジェクトを返す必要があり、そのとおりです_&T_このインスタンスでは不可能です:

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

以下も参照してください。

25
Steve Klabnik

これはあなたが探していたものだと思います。 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());
}
4

ジェネリックと静的ディスパッチを使用して(これらが正しい用語であるかどうかはわかりません。他の誰かが使用しているのを見ただけです)、このようなものを作成できます。

これは正確に「特性として戻る」というわけではありませんが、関数が特性を一般的に使用できるようにします。私の意見では、構文は少しあいまいなので、見落としがちです。

Iteratorトレイトを返すことについて 特定のリストタイプの代わりに一般的なイテレータを使用する を尋ねました。醜くなります。

遊び場

struct MyThing {
    name: String,
}

trait MyTrait {
    fn get_name(&self) -> String;
}

impl MyTrait for MyThing {
    fn get_name(&self) -> String {
        self.name.clone()
    }
}

fn as_trait<T: MyTrait>(t: T) -> T {
    t
}

fn main() {
    let t = MyThing {
        name: "James".to_string(),
    };
    let new_t = as_trait(t);

    println!("Hello, world! {}", new_t.get_name());
}
2
jocull