Trait 的静态工厂方法

Kir*_*ill 4 factory-method rust

我刚刚学习 Rust,所以也许我没有正确理解一些概念。

我有一些实现的特点:

trait Abstract {
    fn name(&self) -> &str;
}

struct Foo {}
struct Bar {}
struct Baz {}

impl Abstract for Foo {
    fn name(&self) -> &str { "foo" }
}
impl Abstract for Bar {
    fn name(&self) -> &str { "bar" }
}
impl Abstract for Baz {
    fn name(&self) -> &str { "baz" }
}
Run Code Online (Sandbox Code Playgroud)

我想向此特征添加一个静态方法,以按名称创建一些标准实现:


trait Abstract {
    fn new(name: &str) -> Self {
        match name {
            "foo" => Foo{},
            "bar" => Bar{},
            "baz" => Baz{},
        }
    }
    fn name(&self) -> &str;
}
Run Code Online (Sandbox Code Playgroud)

但这段代码无法编译,因为:

6 |     fn new(name: &str) -> Self {
  |                           ^^^^ doesn't have a size known at compile-time
Run Code Online (Sandbox Code Playgroud)

我尝试使用 returnfn new(name: &str) -> impl Abstractfn new<T: Abstract>(name: &str) -> T- 但这些变体对于其他错误也不起作用。

在 Rust 中为特征创建工厂方法的正确方法是什么?

cam*_*024 8

在 Rust 中,每个变量都必须是单一的特定类型。这与 OO 语言不同,在面向对象语言中,变量可以具有类型Foo,这意味着该变量包含Foo或 的任何子类Foo

Rust 不支持这一点。如果变量具有 type Foo,则它必须包含 a Foo(忽略任何不安全的问题)。

Rust 也不支持使用特征作为类型(没有关键字dyn)。

在您的示例中,您有:

trait Abstract {
  fn new(name: &str) -> Self {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这里的返回类型Self意味着“无论该特征正在实现的类型”。但是,通过在特征定义中提供主体,您将提供默认实现,因此理论上该函数应该适用于任何类型,因此编译器没有有关真实具体类型的信息Self,因此Sized它不满足边界(这在实践中是非常严格的,可能不是你想要的)。

如果您想要一个接受字符串并返回“T实现某种类型Abstract”的函数,您可以使用“特征对象”,它大致如下所示:

// outside trait definition
fn new_abstract(name: &str) -> Box<dyn Abstract> {  // dyn keyword opts into dynamic dispatch with vtables
  match name {
    "foo" => Box::new(Foo {}),
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,我警告不要使用这种模式。动态分派有一些运行时开销,并且会阻止许多编译时优化。相反,可能有一种更“生疏”的方式来做到这一点,但如果没有更多的上下文,很难说清楚。

一般来说,基于字符串的值构建类型有点像反模式。