Generics 变量参数化的特征不是结构?

Generics 变量参数化的特征不是结构?,generics,rust,polymorphism,trait-objects,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(&am

我正试图了解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
}
trait CanGetTitle{
fn获取标题(&self)->字符串;
}
结构蜘蛛{
pub解析器:T
}
结构GoogleParser;
谷歌解析器的impl CanGetTitle{
fn获取标题(&self)->字符串{
“title from H1.”to_string().clone()
}
}
结构YahooParser;
为YahooParser申请许可证{
fn获取标题(&self)->字符串{
“title from H2.”to_string().clone()
}
}
枚举站点名{
谷歌,
雅虎,,
}
impl站点名{
fn from_url(url:&str)->SiteName{
网站名::谷歌
}
}
fn main(){
让url=”http://www.google.com";
让site_name=SiteName::from_url(&url);
让spider:spider=匹配站点名称{
Google=>Spider{parser:GoogleParser},
Yahoo=>Spider{parser:YahooParser}
};
spider.parser.get_title();//失败
}
我得到了一个关于
match
返回
Spider
s的错误,该参数在两种不同类型上进行了参数化。它希望它返回
Spider
,因为这是模式匹配的第一个臂的返回类型

我如何声明
spider
应该是任何
spider

我如何声明
spider
应该是任何
spider

你不能。简单地说,编译器不知道要分配多少空间来存储堆栈上的
spider

相反,您需要使用:

impl CanGetTitle for Box
哪里
T:CanGetTitle,
{
fn获取标题(&self)->字符串{
(**self.get_title()
}
}
fn main(){
让内部:Box=匹配SiteName::Google{
SiteName::Google=>Box::new(GoogleParser),
SiteName::Yahoo=>Box::new(YahooParser),
};
设spider=spider{parser:innards};
}
我如何声明
spider
应该是任何
spider

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

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

通常可以重新调整对象,这样您就可以使用单态类型。在您的情况下,一种方法是避免临时赋值给多态变量,并且仅在您知道其具体类型的位置使用该值:

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 })
    };
}
fn do_东西(spider:spider){
println!(“{:?}”,spider.parser.get_title());
}
fn main(){
让url=”http://www.google.com";
让site_name=SiteName::from_url(&url);
匹配站点名称{
SiteName::Google=>do_stuff(Spider{parser:GoogleParser}),
SiteName::Yahoo=>do_stuff(Spider{parser:YahooParser})
};
}
请注意,每次调用
do_stuff
时,
T
解析为不同的类型。您只编写了一个
do_stuff
的实现,但编译器会将其单组化两次—对于您调用它的每种类型一次


如果使用
,则必须在
中查找对
解析器.get_title()
的每次调用。但是这个版本通常会更快,因为它避免了查找的需要,并且允许编译器在每种情况下内联
解析器.get_title()
的主体。

我仍然在努力解决这个问题。但是,它会与多种特征一起工作吗?我需要
ParsePage
GetQuery
等东西,并且需要一些可以扩展的东西来覆盖所有需要实现的特性。@jbrown为什么你认为它不能处理多个特性?出于某种原因,我还需要在
Spider
中添加
?size
,就像在
struct Spider
中一样。很高兴知道这一点,非常感谢。@jbrown:T
T
这个具体的
大小应该不需要。我认为在这种情况下,虽然我想在站点之间做的事情有很多共同点,但唯一的区别是,根据站点具体使用哪些HTML选择器来提取我需要的数据,等等。在需要访问数据时,会以额外的指针间接寻址为代价=>实际上,这是你花最少的钱。更大的代价是,暴露出一个足够聪明的优化器来实现调用的虚拟化,这会抑制内联,内联是优化的关键促成因素。因此,虽然额外的指针解引用/虚拟调用的成本非常小,但内联和优化的损失(在紧密循环中)确实是非常昂贵的。@MatthieuM。谢谢,我做了一个调整来说明这一点。
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 })
    };
}