如何在Rust中定义指向Trait函数的指针

3
假设我有以下代码:
trait Saluter {
    fn hello(&self);
    fn bye(&self);
}

struct A {}
struct B {}

impl Saluter for A {
    fn hello(&self) {
        println!("Hello A!");
    }

    fn bye(&self) {
        println!("Bye A!");
    }
}

impl Saluter for B {
    fn hello(&self) {
        println!("Hello B!");
    }

    fn bye(&self) {
        println!("Bye B!");
    }
}

pub struct Foo {
    saluter: Box<dyn Saluter>,
    salute: fn (&dyn Saluter),
}

fn main() {
    let x = Foo {saluter: Box::new(A{}), salute: Saluter::hello};
    let y = Foo {saluter: Box::new(B{}), salute: Saluter::bye};
    (x.salute)(x.saluter.borrow()); //Should print "Hello A!"
    (y.salute)(y.saluter.borrow()); //Should print "Hello B!"
}

基本上,我正在尝试独立地操作调用者和被调用的方法。但是我遇到了以下编译错误:
let x = Foo {saluter: Box::new(A{}), salute: Saluter::hello};
   |                                                  ^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'r> fn(&'r (dyn Saluter + 'r))`
              found fn pointer `for<'r> fn(&'r dyn Saluter)`

显然,y也存在同样的错误。我最近才开始学习Rust,所以对于Rust生命周期参数并不熟练,但我觉得它在抱怨这个问题,尽管我无法确定(dyn Salute + 'r)确切的含义。

有没有办法实现我想要的功能,或者在Rust中根本不可能实现?在C++中,我可以轻松地使用指向基类的指针来完成,但我正在努力找到如何在Rust中实现这一点。

2个回答

0

你在你的Foo结构体中使用了一个由Saluter限定的泛型类型:

pub struct Foo<T> where T: Saluter {
    saluter: Box<T>,
    salute: fn (&T),
}

游乐场


如果OP因某些原因需要使用特质对象,那么这无济于事。 - Peter Hall
如果我理解正确的话,那么在这种情况下,我将无法使Foo在不同的情况下具有不同的行为,这意味着我要么有一个Foo<A>,要么有一个Foo<B>。这意味着我将无法在另一个结构中存储一个通用的Foo,并通过程序动态地定义它的行为,因为我要么只能存储一个特定的Foo<T>,要么同时存储Foo<A>和Foo<B>,并根据给定的情况使用适当的一个,这并不理想。 - Marcos Rolando
@MarcosRolando 你是对的,但是如果你允许 T 是无大小限制的(通过在 trait 约束中添加 ?Sized),你可以将 trait 对象用作 T,从而实现原始目标,并在不需要动态多态性时获得速度提升。Playground - Aiden4

0

我通过仅限制函数指针类型,成功编译了您的代码:

pub struct Foo {
    saluter: Box<dyn Saluter>,
    salute: fn (&(dyn Saluter + 'static)),
}

这个额外的+ 'static约束意味着存储在Foo中的salute函数指针只能与实现Saluter的类型一起使用,这些类型没有有限的生命周期 - 粗略地说,"不包含非静态引用"。也就是说,如果你有

struct B<'x> { ... }
impl<'x> Saluter for B<'x> {}

那么该函数将无法使用。但这是不常见的情况。

不幸的是,我不确定为什么函数指针Saluter :: hello 不能成为原始代码所暗示的完全通用类型,

    salute: for<'a> fn (&'a (dyn Saluter + 'a)),

所以我无法给出完整的解释。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接