Подтвердить что ты не робот

Как вернуть экземпляр признака из метода?

Я пытаюсь создать функцию, которая возвращает экземпляр черты 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 {
  |                                ^^^^^^ does not 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>?

4b9b3361

Ответ 1

Ржавчина 1,26 и выше

impl Trait Теперь существует:

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

У этого есть ограничения, такие как невозможность использования в методе признаков, и он не может использоваться, когда конкретный тип возврата является условным. В этих случаях вам нужно использовать ответ на объект объекта объекта ниже.

Rust 1.0 и выше

Вам нужно вернуть какой-либо объект-признак, например, &T или Box<T>, и вы правы, что &T невозможно в этом случае:

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

Смотрите также:

Ответ 2

Я думаю, это то, что вы искали; простая фабрика, реализованная в 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());
}

Ответ 3

Я думаю, вы можете использовать generics и static dispatch (я понятия не имею, являются ли эти правильные термины, я просто видел, как кто-то их использовал), чтобы создать что-то вроде этого.

Это не совсем "возвращение как черта", но это позволяет функциям использовать общие черты. Синтаксис, по-моему, немного неясен, поэтому его легко пропустить.

Я попросил использовать общие итераторы вместо конкретных типов списков о возврате признака 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());
}