Dyn trait here but not there

Hello!

#[derive(Clone, Copy, Debug)]
pub struct PortRange {
    first: u16,
    last: u16,
}

pub trait ToPortRange {
    fn to_port_range(&self) -> PortRange;
}

pub struct AllPorts {}

impl ToPortRange for AllPorts {
    fn to_port_range(&self) -> PortRange {
        PortRange {
            first: 1,
            last: 65353,
        }
    }
}

struct BrightLightUdpClientBuilder<R> {
    port_ranges: R,
}

impl<'a, R> BrightLightUdpClientBuilder<R>
where
    R: IntoIterator<Item = &'a dyn ToPortRange>,
{
    fn new(port_ranges: R) -> Self {
        Self {
            port_ranges,
        }
    }
    fn build(self) {
        for pr in self.port_ranges.into_iter() {
            println!("{:?}", pr.to_port_range());
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!();

    let port_ranges: Option<&dyn ToPortRange> = Some(&AllPorts{});
    let builder = BrightLightUdpClientBuilder::new(port_ranges);
    builder.build();

    // --- Why does this not work when the one above does?
    let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{}));
    //builder.build();
/*
error[E0271]: type mismatch resolving `<Option<&AllPorts> as IntoIterator>::Item == &dyn ToPortRange`
   --> src\main.rs:113:19
    |
113 |     let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{}));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn ToPortRange`, found struct `AllPorts`
    |
    = note: expected reference `&dyn ToPortRange`
               found reference `&AllPorts`
note: required by a bound in `BrightLightUdpClientBuilder::<R>::new`
   --> src\main.rs:88:21
    |
88  |     R: IntoIterator<Item = &'a dyn ToPortRange>,
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `BrightLightUdpClientBuilder::<R>::new`
89  | {
90  |     fn new(port_ranges: R) -> Self {
    |        --- required by a bound in this
*/

    println!();
    Ok(())
}

Playground.

I'd really like let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{})); to work (in addition to being able to pass a Vec). I just cannot find an incantation that appeases the compiler.

Is it possible to coerce the &AllPorts to be a &dyn ToPortRange?

You can cast it to a trait object with as:

let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{} as &dyn ToPortRange));

The compiler's just not making this coercion on its own

1 Like

You can tell the compiler you want it to infer some coercion via cast like so:

-    let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{}));
+    let builder = BrightLightUdpClientBuilder::new(Some(&AllPorts{} as _));

And then the compiler can figure it out for this example.


The top version works because you're initializing with an explicit type. The original bottom version does not work implicitly as such implicit coercions are not performed when matching traits.

3 Likes

Thank you both!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.