简体   繁体   English

将可变特征对象的向量传递给函数

[英]Pass vector of mutable trait objects to function

I am quite new to Rust, coming from a Java background. 我对Rust相当陌生,来自Java背景。 I am trying to implement the observer pattern in Rust (I guess this is not idiomatic Rust, though). 我正在尝试在Rust中实现观察者模式(不过,我想这不是惯用的Rust)。 My attempt is like this: 我的尝试是这样的:

use crate::observable::{Listener, trigger_listeners};

mod observable {
    pub trait Listener {
        fn trigger(&mut self);
    }

    pub fn trigger_listeners(listeners: Vec<&mut Box<dyn Listener>>) {
        for mut x in listeners {
            x.trigger();
        }
    }
}

struct Mock {
    times_called: u32
}
impl Listener for Mock {
    fn trigger(&mut self) {
        self.times_called += 1;
    }
}
#[test]
fn test() {
    let mut mock = Box::new(Mock{ times_called: 0 });
    trigger_listeners(vec![&mut mock]);

    assert_eq!(mock.times_called, 1)
}

Every listener should implement the Listener trait and be passed as an array to the function trigger_listener . 每个侦听器都应实现侦听器特性,并作为数组传递给函数trigger_listener

This gives me the following error: 这给了我以下错误:

error[E0308]: mismatched types
--> src/lib.rs:27:28
|
27 |     trigger_listeners(vec![&mut mock]);
|                            ^^^^^^^^^ expected trait observable::Listener, found struct `Mock`
|
= note: expected type `&mut std::boxed::Box<(dyn observable::Listener + 'static)>`
found type `&mut std::boxed::Box<Mock>`

error: aborting due to previous error

I was thinking that because the Mock implements the trait Listener, I can pass it as a reference. 我当时在想,因为Mock实现了特征监听器,所以我可以将其作为参考传递。

My other try is to only use a Box which is moved ( pub fn trigger_listeners(listeners: Vec<Box<dyn Listener>>) {} ). 我的另一种尝试是仅使用已移动的Box( pub fn trigger_listeners(listeners: Vec<Box<dyn Listener>>) {} )。 This works, but then I cannot access the mock.times_called anymore. 这行得通,但是后来我再也无法访问mock.times_called了。

I've also made some attempts with a Rc , but this didn't work either. 我也尝试过使用Rc ,但这也没有用。

The issue is actually with your trigger_listeners function rather than with the specific typing itself. 实际上,问题出在您的trigger_listeners函数上,而不是特定的键入本身上。

You're starting into the world of generics here and it's worth a read through the https://doc.rust-lang.org/rust-by-example/generics.html to get a better understanding, but to get started all you need to do is modify the signature of your trigger_listeners function a bit. 您从这里开始进入泛型领域,值得一读https://doc.rust-lang.org/rust-by-example/generics.html以获得更好的理解,但请您入门要做的是稍微修改一下一下trigger_listeners函数的签名。

You currently have 您目前有

pub fn trigger_listeners(listeners: Vec<&mut Box<dyn Listener>>) {
        for mut x in listeners {
            x.trigger();
        }
    }

In rust, traits play a double-role where they are also considered types to an extent. 在锈病中,特征扮演双重角色,在某种程度上它们也被视为类型。 So you need to generalize your method signature in order to reflect that. 因此,您需要对方法签名进行概括以反映这一点。


pub fn trigger_listeners<T: Listener>(listeners: Vec<&mut Box<T>>) {
        for mut x in listeners {
            x.trigger();
        }
    }

Here we're saying trigger_listeneres should accept any type T where that type implements the Listener trait versus passing the trait itself in the type signature. 这里我们说的是,trigger_listeneres应该接受任何类型T ,其中该类型实现Listener特性,而不是在类型签名中传递特性本身。


Edit: 编辑:

As trentctl pointed out and based on the fact you need vecs of a non-contigous type to be able to be passed to the trigger_listeners function, we need to modify a few things. 正如trentctl所指出的,并基于您需要非连续类型的vecs才能将其传递给trigger_listeners函数的事实,我们需要做一些修改。

1) We can actually do this without Box in order to simplify things. 1)我们实际上可以在没有Box的情况下执行此操作,以简化操作。

let mut mock = Mock { times_called: 0 };
let mut mock2 = Mock2 { times_called: 0 };

let items: Vec<&mut dyn Listener> = vec![&mut mock, &mut mock2];
trigger_listeners(&mut items);

2) To accept a vec of Unsized dyn Listeners, we need to add the ?Sized trait to the trait bounds of the trigger_listeners function. 2)要接受未调整大小的dyn侦听器的vec,我们需要将?Sized特质添加到trigger_listeners函数的特质范围。

pub fn trigger_listeners<t>(listeners: &mut Vec<&mut T>) 
where T: Listener + ?Sized
{
    for x in listeners {
        x.trigger();
    }
}

Another point, if you aren't expecting the types that you need to pass to the trigger_listener function to change, you can possibly step away from using trait objects and instead wrap your types in an enum type and pass a vec of those instead of trait objects. 还有一点,如果您不希望传递给trigger_listener函数的类型发生更改,则可以放弃使用trait对象,而将您的类型包装为枚举类型,并传递一个vec而不是trait对象。 If, however you're expecting users of your library to extend the known types with their own implementing the Listener trait then trait objects is the right way to go about it. 但是,如果您期望库的用户使用自己实现的Listener trait扩展已知类型,那么trait对象就是解决之道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM