Matching trait returns


#1

My current project is a server for a line protocol, using futures & tokio. My intention was to use a match on the command to select a handling function, which would then return an appropriate future to read command data off the wire and output it.

The plan that each of these functions would return impl Future<...> and the match would take care of the rest. Apparently not all impl Traits are equal, even when they appear to be.

I’m explaining this very poorly. Here’s an example (playground https://is.gd/mQFTux) (hard to show the real example using futures as its a colossal mess right now, but I can put it somewhere if necessary).

#![feature(conservative_impl_trait)]

use std::fmt::Debug;

fn main() {
  let d = match true {
    true => a(),
    false => b(),
  };
  println!("{:?}", d);
}

fn a() -> impl Debug {
  vec!(1,2,3)
}

fn b() -> impl Debug {
  Some(42)
}
error[E0308]: match arms have incompatible types
 --> <anon>:6:11
  |
6 |   let d = match true {
  |           ^ expected anonymized type, found a different anonymized type
  |
  = note: expected type `impl std::fmt::Debug` (anonymized type)
  = note:    found type `impl std::fmt::Debug` (anonymized type)

So that response sort of makes sense to me - they are different types, so its fair that they don’t match. On the other hand I thought I’d made it pretty clear that I only care about the trait, not the whole thing. But I get the the compiler needs to know the real type for sizing and such.

So … what now? :slight_smile:


#2

Dynamic dispatch is the only easy way:

#![feature(conservative_impl_trait)]

use std::fmt::Debug;

fn main() {
  let a_obj;
  let b_obj;
  let d: &Debug = match true {
    true => { a_obj = a(); &a_obj },
    false => { b_obj = b(); &b_obj },
  };
  println!("{:?}", d);
}

fn a() -> impl Debug {
  vec!(1,2,3)
}

fn b() -> impl Debug {
  Some(42)
}

You could also write an enum with two variants and implement Debug on that and forward to the inner value, but meh; that’s tedious.


#3

What is going on with the binding declarations here? I didn’t think this was valid Rust, but playground happily runs it. I can’t find any discussion on it in the reference even!


#4

As long as they’re iniialized before use, and only initialized once, this works fine. I believe the bindings chapter of be book covers it, if not, the new one should?


#5

You are totally right; the book definitely covers it. I’ve always just used an Option in cases where I needed late initialization like that, but this is way nicer! Going to just file this under TIL.


#6

Excuse me, where can I find the specification of using “impl Trait” as a fuction return type?
And I found some strange codes in core::any::Any
https://doc.rust-lang.org/nightly/src/core/up/src/libcore/any.rs.html#139-242
Any is a Trait,but there is an “impl Any” at line 139!!! Only for doc purpose???


#7

It’s unstable, so the only documentation would be in Issue #34511 and RFC #1522.

As for the code for Any, that’s unrelated. That’s implementing methods on the trait’s object type directly, as opposed to on types that implement the trait. It means the methods exist on &Any, but not on &i32, despite i32 implementing Any. I can’t think of any other examples off the top of my head.


#8

Thank you!
I write an example, but it can only call the method with a static variable.
https://is.gd/uhIeLb

trait Boo {
    fn boo(&self) {
        println!("boo");
    }
}
impl Boo {
    fn foo(&self) {
        println!("foo");
    }
    fn koo() {
        println!("koo");
    }
}

struct A;

impl Boo for A {}

fn main() {
    let a = &A;
    a.boo();
    //this will fail
    //(a as &Boo).foo();

    static b: A = A;
    (&b as &Boo).foo();

   //act like a static method
    Boo::koo();
}

#9

It works if you change the trait to trait Boo: 'static { ... }. I honestly couldn’t tell you why, though.

I suspect it might have to do with the trait lifetime (i.e. Box<Trait> is actually Box<Trait + 'static>, &Trait is actually &'a (Trait + 'a)), and adding : 'static (which constrains the implementing type) somehow affects that. I tried a few things to get the same effect, but this was the only thing that worked. Incidentally, this is also how Any is defined.


#10

The reason is that the impl block could be more generic.

Change impl Boo into impl<'a> Boo + 'a (!).