自动导出 Arc 的特征实现

Automatically derive traits implementation for Arc

我有一个特征和一个 struct 实现它。 Arc包裹的struct可以调用trait的方法,但是Arc本身并没有实现:

use std::sync::Arc;

trait Foo{
    fn bar(&self);
}

struct A;

impl Foo for A{ 
    fn bar(&self){ }
}

fn test<A: Foo>(arc_a: Arc<A>){
    let foo_obj: & dyn Foo = &arc_a; //the trait bound `std::sync::Arc<A>: Foo` is not satisfied
}

以下代码工作正常:

use std::sync::Arc;

trait Foo{
    fn bar(&self);
}

struct A;

impl Foo for A{ 
    fn bar(&self){ }
}

impl<A> Foo for Arc<A> //Manually implemented
where
    A: Foo
{ 
    fn bar(&self){ self.bar() }
}

fn test<A: Foo>(arc_a: Arc<A>){
    let foo_obj: & dyn Foo = &arc_a;
}

有没有办法自动派生这种特征的实现?

您必须手动实施它们。虽然您手动实施 Foo for Arc<A> 看起来完全微不足道,但重要的部分是“隐藏”在 self.bar() 中,其中从 self: &Arc<A>&A 发生自动取消引用。请记住,您的实现 Foo for Arc<A> 实际上可能完全不同;不过,在现实世界中,我们通常只需要“deref impl”。

在你的第一个例子中

fn test<A: Foo>(arc_a: Arc<A>){
    let foo_obj: &dyn Foo = &arc_a;
}

如果您执行 = &*arc_a,这将编译,因为 *arc_aArc<A> 取消引用到 A,然后 &*arc_a 是一个 &dyn Foo.

在您的第二个示例中,您提供了手动实施 for Arc<A>

impl<A> Foo for Arc<A> where A: Foo { 
    fn bar(&self){ self.bar() }
}

在这里,deref - 上面是显式的 - 现在隐含在 self.bar() 中。这使您免于手动取消引用,但 impl 可能完全不同。例如:

impl Foo for A{ 
    fn bar(&self){ println!("This is fine") }
}

impl<A> Foo for Arc<A>
where
    A: Foo
{ 
    fn bar(&self) { panic!() }
}

fn test<A: Foo>(arc_a: Arc<A>){
    let foo_obj: & dyn Foo = &arc_a;
    foo_obj.bar()
}

fn main() {
    test(Arc::new(A))
}

上面的程序会崩溃,因为 foo_obj 使用了 impl for Arc<A>。如果将 test 中的行更改为 = &*arc_a,则使用 impl for A,程序将打印“This is fine”。

如果您只需要 Foo 的普通智能指针的微不足道的实现,您可以通过宏来实现:

macro_rules! deref_impl {
    ($($sig:tt)+) => {
        impl $($sig)+ {
            fn bar(&self) {
                (**self).bar()
            }
        }
    };
}
deref_impl!(<'a, N> Foo for &'a N where N: Foo + ?Sized);
deref_impl!(<'a, N> Foo for &'a mut N where N: Foo + ?Sized);
deref_impl!(<N> Foo for Box<N> where N: Foo + ?Sized);
deref_impl!(<N> Foo for std::rc::Rc<N> where N: Foo + ?Sized);
deref_impl!(<N> Foo for std::sync::Arc<N> where N: Foo + ?Sized);