gpt4 book ai didi

generics - 特征对象如何使用通用方法作为参数来接受特征?

转载 作者:行者123 更新时间:2023-12-03 11:41:32 30 4
gpt4 key购买 nike

因此,特征对象不能具有带有泛型的方法-看起来不错。但是在这种语言中,只有通过泛型和特征对象才能使用使用抽象机制的唯一方法。这意味着对于每个特征,我必须事先确定它是否可以用作对象,并在所有地方都使用dyn而不是impl。并且必须采用相同的方式来支持其中的所有特征。这感觉很丑。您能提出任何建议或告诉我为什么要这样设计吗?

fn main() {}

// some abstracted thing
trait Required {
fn f(&mut self, simple: i32);
}

// this trait doesn't know that it's going to be used by DynTrait
// it just takes Required as an argument
// nothing special
trait UsedByDyn {
// this generic method doesn't allow this trait to be dyn itself
// no dyn here: we don't know about DynTrait in this scope
fn f(&mut self, another: impl Required);
}

// this trait needs to use UsedByDyn as a function argument
trait DynTrait {
// since UsedByDyn uses generic methods it can't be dyn itself
// the trait `UsedByDyn` cannot be made into an object
//fn f(&mut self, used: Box<dyn UsedByDyn>);

// we can't use UsedByDyn without dyn either otherwise Holder can't use us as dyn
// the trait `DynTrait` cannot be made into an object
// fn f(&mut self, used: impl UsedByDyn);

// how to use UsedByDyn here?
}

struct Holder {
CanBeDyn: Box<dyn DynTrait>,
}

最佳答案

Which means that for each trait I have to decide beforehand if it can be used as an object at all and use dyn in there everywhere instead of impl.


您可以做到这一点,但幸运的是,这不是唯一的选择。
您也可以像往常一样编写特质,并在适当的地方使用泛型。如果/当您需要特征对象时,请定义一个在本地使用的新的对象安全特征,该特征公开该位置实际需要的API子集。
例如,假设您具有或使用非对象安全特征:
trait Serialize {
/// Serialize self to the given IO sink
fn serialize(&self, sink: &mut impl io::Write);
}
该特征不能用作特征对象,因为它(大概是为了确保最大效率)具有通用方法。但这并不需要阻止您的代码使用trait对象访问trait的功能。假设您需要将 Serialize值装箱以将其保存在向量中,然后将其保存到文件中:
// won't compile
struct Pool {
objs: Vec<Box<dyn Serialize>>,
}

impl Pool {
fn add(&mut self, obj: impl Serialize + 'static) {
self.objs.push(Box::new(obj) as Box<dyn Serialize>);
}

fn save(&self, file: &Path) -> io::Result<()> {
let mut file = io::BufWriter::new(std::fs::File::create(file)?);
for obj in self.objs.iter() {
obj.serialize(&mut file);
}
Ok(())
}
}
上面的代码无法编译,因为 Serialize不是对象安全的。但是-您可以轻松定义一个满足 Pool需求的新的对象安全特征:
// object-safe trait, Pool's implementation detail
trait SerializeFile {
fn serialize(&self, sink: &mut io::BufWriter<std::fs::File>);
}

// Implement `SerializeFile` for any T that implements Serialize
impl<T> SerializeFile for T
where
T: Serialize,
{
fn serialize(&self, sink: &mut io::BufWriter<std::fs::File>) {
// here we can access `Serialize` because `T` is a concrete type
Serialize::serialize(self, sink);
}
}
现在, Pool几乎可以使用 dyn SerializeFile( playground)起作用:
struct Pool {
objs: Vec<Box<dyn SerializeFile>>,
}

impl Pool {
fn add(&mut self, obj: impl Serialize + 'static) {
self.objs.push(Box::new(obj) as Box<dyn SerializeFile>);
}

// save() defined the same as before
...
}
定义一个单独的对象安全特征似乎是不必要的工作-如果原始特征足够简单,那么您当然可以使其成为对象安全的。但是有些特征要么太笼统,要么太面向性能,以至于一开始就不能成为对象安全的对象,在这种情况下,最好记住保持它们的通用性是可以的。当您确实需要对象安全版本时,通常将用于一项具体任务,其中根据原始特征实现的自定义对象安全特征就可以完成任务。

关于generics - 特征对象如何使用通用方法作为参数来接受特征?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64942754/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com