简体   繁体   中英

Adding an iterator of a trait on another trait

I have a trait which has a function that should return an iterator of a different trait. I also need a common function which takes this trait and does operations on it.

This is the code I have currenty:

pub trait NodeLike : Sized {
    /// Get the node id
    fn get_id(&self) -> i32;
}

pub trait EdgeLike {
    /// Get the destination node id
    fn get_node_id(&self) -> i32;

    /// Get the edge id
    fn get_edge_id(&self) -> i32;

    /// iterates of the source nodes
    fn iter_source_nodes(&self) -> dyn Iterator<Item = dyn NodeLike>;
                                                       ^^^^^^^^^^^^^
}

fn operation(id: i32, edge: dyn EdgeLike) {
    for node in edge.iter_source_nodes().find(|n| n.get_id() == id) {
        
    }
}

The underlined part above throws the compiler error:

the trait `NodeLike` cannot be made into an object
`NodeLike` cannot be made into an object rustc E0038
main.rs(1, 22): for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>

Reading through the documentation, I believe my NodeLike trait is object-safe. The only function it has is &Self (ie &self)

Now, I can further my code by removing the Sized trait on NodeLike . However, then the error occurs in the function operation ; I cannot use the find function on the iterator.

How can I change my code so that I can make NodeLike object safe, AND be able to return an iterator which I can call find on?

Following Stargateur's suggestion and your playground link, I got it to compile like this:

Playground link

This is if you do not want to use any unstable features.

The trick is to specify the explicit type of the iterator. If you don't know that type, just put any stupid type in and let the compiler complain about the type mismatch. It'll tell you that we're dealing here with a slice iter, so hence the std::slice::Iter .

The other modifications are then about adding a lifetime parameter to your struct, because iter() returns references. If you don't want that, change it to into_iter , but then you must use self instead of &self .

Also, for your operation, you must do some lifetime magic...

fn operation<'a, Item: NodeLike>(id: i32, edge: &'a impl EdgeLike<'a, Item=Item> )

If you are okay with unstable features, you can avoid having to specify the exact type of the iter: In your playground version just change the dyn to impl . The compiler will tell you that that's an unstable feature and will show you the macro to use.

Finally, to avoid having to add lifetime annotations to the trait itself, there's something about generic associated types, but maybe that's going too far for now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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