英文:
Function argument implementing two traits
问题
pub trait Router {
fn route(&self);
}
pub trait Library {
fn do_math(&self);
}
pub struct Pair1 {}
impl Library for Pair1 {
fn do_math(&self) {
println!("Doing math");
}
}
impl Router for Pair1 {
fn route(&self) {
println!("Routing");
}
}
pub struct Pair2 {}
impl Library for Pair2 {
fn do_math(&self) {
println!("Doin math");
}
}
impl Router for Pair2 {
fn route(&self) {
println!("Routin");
}
}
pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}
pub fn my_fn(pair: &mut Box<dyn RouterAndLibrary>) {
println!("Hello");
}
pub fn wrapper_fn() {
let pair = Pair1 {};
my_fn(&mut Box::new(pair));
}
英文:
So let's take this code with two structs implementing two traits. How can I make a function that takes as argument any struct that implement both traits ?
pub trait Router {
fn route(&self);
}
pub trait Library {
fn do_math(&self);
}
pub struct Pair1 {}
impl Library for Pair1 {
fn do_math(&self) {
println!("Doing math");
}
}
impl Router for Pair1 {
fn route(&self) {
println!("Routing");
}
}
pub struct Pair2 {}
impl Library for Pair2 {
fn do_math(&self) {
println!("Doin math");
}
}
impl Router for Pair2 {
fn route(&self) {
println!("Routin");
}
}
Here is the solution I tried:
pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}
pub fn my_fn(
pair: &mut Box<dyn RouterAndLibrary>
) {
println!("Hello");
}
pub fn wrapper_fn() {
let pair = Pair1 {};
my_fn(pair);
}
But this throws an error: mismatched types expected mutable reference "&mut Box<(dyn RouterAndLibrary + 'static)> found struct Pair1
.
答案1
得分: 3
如评论中所指出,尽量在可能的情况下使用泛型而不是特征对象。静态分发的泛型使这种情况变得容易。
pub fn my_fn<T: Router + Library>(pair: &T) {
println!("Hello");
}
这是简单的解决方案,通常情况下,应该优先考虑使用泛型。
如果你确实需要一个 dyn
特征对象,那么你已经发现了最佳的方法。在特征对象类型中不能要求多个特征(例外情况是 Send
和 Sync
,它们已经内置到编译器中),因为宽指针中的虚拟分派表与单个特征关联。所以我们需要创建一个新的特征,其中包含正确的分派信息。我们可以很容易地这样做,而且还可以提供一个通用实现,就像你已经发现的那样。
pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}
你对 Box
有点过分了。引用本身可以包含不定大小的类型,所以你的 dyn
解决方案将如下所示。
pub fn my_fn(pair: &dyn RouterAndLibrary) {
println!("Hello");
}
在任何情况下,我们都可以这样调用函数
my_fn(&pair);
前一个函数签名将进行单态化(monomorphize),而后者将自动为我们生成一个特征对象,无需显式转换。
英文:
As noted in the comments, you should probably favor generics instead of trait objects when possible. Statically-dispatched generics make this situation easy.
pub fn my_fn<T: Router + Library>(pair: &T) {
println!("Hello");
}
That's the easy solution, and generally you should favor generics when possibly.
If you truly need a dyn
trait object, then you've already discovered the best way to do it. You can't require multiple traits in a trait object type (with the exception of Send
and Sync
, which are baked into the compiler), because the virtual dispatch table in the wide pointer is associated to a single trait. so we need to make a new trait that contains the right dispatch information. We can easily do that and provide a blanket implementation, as you've already discovered.
pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}
You just got a little carried away with Box
. References themselves can contain unsized types, so your dyn
solution would look like this.
pub fn my_fn(pair: &dyn RouterAndLibrary) {
println!("Hello");
}
In either case, we can call the function as
my_fn(&pair);
The former function signature will monomorphize, and the latter will automatically produce a trait object for us with no explicit cast required.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论