Generics 变量参数化的特征不是结构?
我正试图了解Rust的仿制药。我正在写一些东西来从不同的网站提取HTML。我想要的是这样的东西: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
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:TT
这个具体的大小应该不需要。我认为在这种情况下,虽然我想在站点之间做的事情有很多共同点,但唯一的区别是,根据站点具体使用哪些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 })
};
}