Infer the generic return value type if unused

Let's assume I have a system like that:

trait FromBuffer {
    fn from_buffer(buffer: &[u8]) -> Self;
}

fn request<RetType: FromBuffer>() -> RetType {
    let buffer = b"to implement: do request here";
    RetType::from_buffer(buffer)
}

fn main() {
    request();
}

I am doing a request, and via trait it will transform the result in any type I need, assuming the type implements FromBuffer.

In many cases this will magically do exactly what I want, but when I want to discard the result (no assignment to a variable) Rust still insists, that I manually specify the generic type: note: cannot satisfy _: FromBuffer

To me it seems there is no way to make this work without a turbofish or something like let _: () = .... Obviously Rust is not wrong here, but I wonder why can't the return type in the case of no assignment be infered to be ()? Most likely there is a good reason for that?

because it's a generic type, there's potentially multiple candidate types can be used, and it will affect the generated code, which will have observable behavior to the program state. in your example, different types can have different implementation for the FromBuffer trait, the compiler doesn't know which one to pick.

in your particular case, it might be possible to arbitrarily pick one type (e.g. the unit type ()) and the resulted code can be the same, but this is a special case which cannot be made into a reasonable general rule.

in general, it's not possible to arbitrarily pick a type in a unconstraint generic context, because what type to pick can change the behavior of the progarm. here's a contrived example:

// the bound for the return type, similar to `Default`
trait ReturnType {
    from_thin_air() -> Self;
}
struct Foo {}
struct Bar {}
impl ReturnType for Foo { println!("do this"); Foo {} }
impl ReturnType for Bar { println!("do that"); Bar{} }
fn value_from_thin_air<Ret: ReturnType>() -> Ret { Ret::from_thin_air() }
fn main() {
    // what return type should the compiler pick ???
    value_from_thin_air();
}

// what's more, even if the `ReturnType` trait is all implemented exactly the same way
// the program behavior can still be changed depending on what concrete type to pick
// for example, they might have desctructors
impl Drop for Foo {
    fn drop(&mut self) { println!("drop this"); }
}
impl Drop for Bar {
    fn drop(&mut self) { println!("drop that"); }
}
2 Likes

Incidentally Rust had (technically still has) default type parameters on functions, but they wouldn't help on their own here due to issue 98931. There was a plan for default type parameter fallback, too, but it stalled out and will probably need restarted from scratch if it's going to come back.

For the OP example you can just exclude the semicolon at the call site and () will be inferred from main's return type :wink:.

2 Likes

As a slightly less contrived example, it would seem quite reasonable on the surface for any type implementing FromBuffer to assert! that the given buffer contains an appropriate number and format of bytes to construct the type. Without a type annotation of some kind, the compiler doesn't whether from_buffer() should return normally or panic on any given buffer.

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.