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