generics - 通过特征而不是结构参数化变量?

标签 generics rust polymorphism trait-objects

我正在努力研究 Rust 的泛型。我正在写一些东西来从不同的网站中提取 HTML。我想要的是这样的:

trait CanGetTitle {
    fn get_title(&self) -> String;
}

struct Spider<T: CanGetTitle> {
    pub parser: T
}

struct GoogleParser;
impl CanGetTitle for GoogleParser {
    fn get_title(&self) -> String {
        "title from H1".to_string().clone()
    }
}

struct YahooParser;
impl CanGetTitle for YahooParser {
    fn get_title(&self) -> String {
        "title from H2".to_string().clone()
    }
}

enum SiteName {
    Google,
    Yahoo,
}

impl SiteName {
    fn from_url(url: &str) -> SiteName {
        SiteName::Google
    }
}

fn main() {
    let url = "http://www.google.com";
    let site_name = SiteName::from_url(&url);
    let spider: Spider<_> = match site_name {
        Google => Spider { parser: GoogleParser },
        Yahoo => Spider { parser: YahooParser }
    };

    spider.parser.get_title();    // fails
}

我收到关于 match 的错误返回 Spider s 参数化了两种不同的类型。它期望返回 Spider<GoogleParser>因为那是模式匹配第一臂的返回类型。

我如何声明 spider应该是任何 Spider<T: CanGetTitle>

最佳答案

How can I declare that spider should be any Spider<T: CanGetTitle>?

只是补充一点@Shepmaster 已经说过的话,spider不能是任何 Spider<T> ,因为它必须正好是一个 Spider<T> . Rust 使用单态化(解释 here )实现泛型,这意味着它为使用的每个具体类型编译一个单独版本的多态函数。如果编译器无法推断出唯一的 T对于特定的调用站点,这是一个编译错误。在您的情况下,编译器推断类型必须是 Spider<Google> , 但下一行试图将其视为 Spider<Yahoo> .

使用特征对象可以让您将所有这些推迟到运行时。通过将实际对象存储在堆上并使用 Box , 编译器知道有多少空间需要被分配到栈中(恰好是 Box 的大小)。但这会带来性能成本:当需要访问数据时存在额外的指针间接,更重要的是,优化编译器无法内联虚拟调用。

通常可以重新调整事物,以便您无论如何都可以使用单态类型。在您的情况下,一种方法是避免临时分配给多态变量,并仅在您知道其具体类型的地方使用该值:

fn do_stuff<T: CanGetTitle>(spider: Spider<T>) {
    println!("{:?}", spider.parser.get_title());
}

fn main() {
    let url = "http://www.google.com";
    let site_name = SiteName::from_url(&url);
    match site_name {
        SiteName::Google => do_stuff(Spider { parser: GoogleParser }),
        SiteName::Yahoo => do_stuff(Spider { parser: YahooParser })
    };
}

注意每次 do_stuff被称为,T解析为不同的类型。你只写一个 do_stuff 的实现,但是编译器将它单态化了两次 - 对于您调用它的每种类型一次。

如果您使用 Box然后每次调用parser.get_title()必须在 Box 中查找的 vtable .但是这个版本通常会更快,因为它避免了查找的需要,并允许编译器内联 parser.get_title() 的主体的可能性。在每种情况下。

关于generics - 通过特征而不是结构参数化变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41383790/

相关文章:

java - 如何将非泛型类集成到泛型层次结构中?

closures - 如何从闭包中返回对闭包参数的引用?

string - 遍历字符串中的行,包括换行符

c++ - 检查多态性中的真实变量类型 (C++)

ruby-on-rails-3 - HABTM 多态关系

c# - 如何约束泛型类型必须有一个采用特定参数的构造函数?

java - 调用接受 Class<T> 并返回 T 的泛型方法时,如何返回特定类型的列表?

java - 为什么 javac 不能为用作参数的函数推断泛型类型参数?

c - 在多个线程中独立运行 Boehm GC

java - 在 ovverriden 方法中使用多态泛型