What the route type is?

Ok, I just ask AI to give me some example code, as it is:

use rtnetlink::*;
use tokio_stream::*;

#[tokio::main]
async fn a() -> Result<(), Box<dyn std::error::Error>> {
    let (connection, handle, _) = new_connection()?;
    tokio::spawn(connection);

    let mut routes = handle.route().get(IpVersion::V4).execute();

    while let Some(route) = routes.next().await {
        route; //.unwrap();
    }

    Ok(())
}

The problem is, I don't know what the type of route; RA tell me it is some type of let route: <impl TryStream<Ok = RouteMessage, Error = Error> as Stream>::Item, but I don't know what exactly it is.

Ok = RouteMessage, Error = Error indicates it is something like Result<RouteMessage, Error>, but when I call route.unwrap(), the code was refused.

I don't know how to deal with this. And for a general rule, what can I do? I guess I should import some trait? use some crate::struct?? No clue here for me to find import which.

What was the exact error when you tried unwrap()?

Using the docs: RouteGetRequest in rtnetlink - Rust

routes is an opaque type (impl TryStream<Ok = RouteMessage, Error = Error>) that cannot be named. RA is correct about that.

You probably want to use the try_next() method from the TryStreamExt trait, instead of next() from the StreamExt trait. I believe the latter only works if the Result can be named.

1 Like
error[E0308]: mismatched types
  --> myip/src/lib.rs:17:13
   |
16 |         match route {
   |               ----- this expression has type `<impl TryStream<Ok = netlink_packet_route::route::message::RouteMessage, Error = rtnetlink::Error> as Stream>::Item`
17 |             Ok(x) => todo!(),
   |             ^^^^^ expected associated type, found `Result<_, _>`
   |
  ::: /home/zylthinking/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/rtnetlink-0.14.1/src/route/get.rs:68:29
   |
68 |     pub fn execute(self) -> impl TryStream<Ok = RouteMessage, Error = Error> {
   |                             ------------------------------------------------ the expected opaque type
   |
   = note: expected associated type `<impl TryStream<Ok = netlink_packet_route::route::message::RouteMessage, Error = rtnetlink::Error> as Stream>::Item`
                         found enum `Result<_, _>`
   = help: consider constraining the associated type `<impl TryStream<Ok = netlink_packet_route::route::message::RouteMessage, Error = rtnetlink::Error> as Stream>::Item` to `Result<_, _>`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

I guess this could be a rustc weakness; at least, it is possible for rustc to get the correct type.
I have read the source, be sure the type must be Result<RouteMessage, Error>.

It's not actually, not in a SemVer-respecting way.

TryStream has Stream as a supertrait bound, but with no conditions on what the Item is. The only reason the Item must be a Result<<_>::Ok, <_>::Error> is because of

  • the blanket implementation when this is the case being the only current implementation, and
  • the trait being sealed

But it would be a non-breaking change for the crate to add more implementations where the Item is something else, or to unseal TryStream so that others could do that. So downstream code can't assume this is true.

(Sealing is a big of a hack anyway, albeit a sanctioned one. I don't think trait solving takes it into consideration at all.)


Either this was by design, so you have to use try_poll_next from a practical point of view (as you can't know the Item type but you do know the return type of try_poll_next). Or someone made a mistake.[1] I agree it's surprising anyway. If it was by design, the documentation should be updated to explain it.


  1. Either the futures maintainers by not committing to Item being Result<_, _> by making it part of the bound, or the rtnetlink maintainers by not adding that bound to their opaque type. An update to futures would be technically breaking (but probably not in a way anyone would care about), but I believe an update to rtnetlink would be fine. ↩︎

2 Likes

Looks like it's due to historical compiler limitations.

Edit: I don't know if this pr hits all occurrences in the crate, but if so it should fix this issue.

https://github.com/rust-netlink/rtnetlink/pull/86

Does the code means although T/E can be any type freely, an TryStream<T, E> always implements Stream<Item = Result<T, E>>;

So S.next() should aways returns Result<T, E>; changing T, E to other types will not change this

impl<S, T, E> TryStream for S
where
    S: ?Sized + Stream<Item = Result<T, E>>,
{
    type Ok = T;
    type Error = E;

    fn try_poll_next(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Self::Ok, Self::Error>>> {
        self.poll_next(cx)
    }
}

Except partial template will be supported in the future, but even then, calling S.next will need to find which stream's next should be called, and you have to provide T/E explicitly. Then, Result<T, E> still can be resolved

I can participate by mobile only for the next few days, so it will be a pain for me to try to demonstrate what I meant in code. But maybe you can help me out.

Mock up the TryStream trait in the playground, copying its definition and the blanket implementation. Then choose or mock up a Stream implementor where the Item is not a Result (so the blanket implementation does not apply). Finally, implement TryStream for that type.

It should be possible to make this compile, demonstrating that an implementation that does not have a Result as Item is possible. Adding that impl is supposed to be a non-breaking change. Thus the compiler cannot assume Item is a Result.

(If you or whomever partially completes a playground, I may try to complete it.)

1 Like

You are correct, I need sometime to understand this

1 Like