使用具有特征的泛型

Using generics with trait

我正在尝试实现包含通用函数的特征。但是,我无法在已实现的函数中实例化结构。

trait Extractor {
    fn parse(&self) -> String;
}

struct BooksExtractor {}

impl Extractor for BooksExtractor {
    fn parse(&self) -> String {
        "test".to_owned()
    }
}

struct Pass<E: Extractor> {
    pub extractor: Option<E>,
    pub name: String,
    pub next_pass: Option<Box<Pass<E>>>,
}

trait Robot: Sized {
    fn get_pass<E: Extractor>(&self) -> Pass<E>;
}

struct GutenbergRobot {}

impl Robot for GutenbergRobot {
    fn get_pass<E: Extractor + ?Sized>(&self) -> Pass<E>
        where E: Extractor + Sized {
        Pass {
            extractor: Some(BooksExtractor {}),
            name: "test".to_owned(),
            next_pass: None
        }
    }
}

当尝试在已实现的函数中实例化 Pass 结构时,编译器抱怨参数应为 extractor 字段的类型:

error[E0308]: mismatched types
  --> src\main.rs:33:29
   |
33 |             extractor: Some(BooksExtractor {}),
   |                             ^^^^^^^^^^^^^^^^^ expected type parameter, found struct `BooksExtractor`
   |
   = note: expected type `E`
              found type `BooksExtractor`

您的 get_pass 函数签名指定它将构造一个 Pass<E> 类型。这意味着它应该在 Pass<E: Extractor + Sized> 有效的任何地方工作。

假设除了代码之外还有以下结构:

struct OtherBooksExtractor {}
impl Extractor for OtherBooksExtractor {
    fn parse(&self) -> String {
        "foobar".to_owned()
    }
}

那么您将期望以下仅基于类型起作用:

fn bla() -> Pass<OtherBookExtractor> {
   GutenbergRobot.get_pass()
}
assert_eq!(bla.extractor.parse(), "foobar")

但是,您正在构建比 get_pass 中的通用 Pass<E> 更具体的内容:一个 Pass<BooksExtractor>!如果允许的话,那就意味着我写的代码不会像我预期的那样工作。

您的 Robot::get_pass 函数不应该是通用的。相反,您应该使用关联类型:

trait Robot: Sized {
    type Extractor: Extractor;
    fn get_pass(&self) -> Pass<Self::Extractor>;
}

impl Robot for GutenbergRobot {
    type Extractor = BooksExtractor;
    fn get_pass(&self) -> Pass<BooksExtractor> {
        Pass {
            extractor: Some(BooksExtractor {}),
            name: "test".to_owned(),
            next_pass: None
        }
    }
}

简而言之,当 caller 可以选择函数返回的类型时,您应该使用泛型,当 implementer[=21] 时,您应该使用关联类型=]选择类型。

另见