Trait with a generic parameter won't compile if non-empty

Here's something I can't get to compile. I'm instantiating a trait from rend3-framework. I'm the user of the trait and its implementation, not the author of the Rend3 crates.

This is a minimized example that generates the same error as the real program.

By default, App's trait T is (), or empty. That compiles and works.
If I instantiate it with () explicitly, that works, too.
If I instantiate it with a nonempty type, I get the compile error shown here.

The puzzling thing is that Ui is definitely an App. I can call its hello method, and that works. So the compiler knows Ui is an App. But the call to the non-method function, "start", won't compile. That call is inside the Rend3 framework crate. I'm just instantiating the generic App.

What am I missing here?


//  Trait with a generic parameter.
pub trait App<T: 'static = ()> {

    fn hello(&self, val: T);    // trait fn, must be implemented
}

#[derive(Debug)]
enum Color { Red(String), _Green, _Blue }

#[derive(Default)]
struct Ui {
    _v: String
}

impl App<Color> for Ui {
    //  Trait fn implementation
    fn hello(&self, val: Color) { println!("Hello from Ui: {:?}", val) }
}

//  Not part of the impl, just wants an App parameter.
pub fn start<A: App + 'static>(app: A) {
    println!("Start called.");
}

fn main() {
    let ui = Ui::default();
    ui.hello(Color::Red("on".to_string())); // no problem, Ui is an App.
    start(ui);  // compile error E0277
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Ui: App` is not satisfied
  --> src/main.rs:29:11
   |
29 |     start(ui);  // compile error E0277
   |     ----- ^^ the trait `App` is not implemented for `Ui`
   |     |
   |     required by a bound introduced by this call
   |
   = help: the following implementations were found:
             <Ui as App<Color>>
note: required by a bound in `start`
  --> src/main.rs:22:17
   |
22 | pub fn start<A: App + 'static>(app: A) {
   |                 ^^^ required by this bound in `start`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

In the function start, the bound requires A to implement App<()>. But you have implemented App<Color> for Ui. Either update your bounds or implement App<()> for Ui.

1 Like

OK. So see

So i have to implement Ui for App, but it will never be called.

App for Ui?

Well, whether it is called or not is irrelevant. The bound on start requires A to implement App<()>. Now if start decides to do nothing with the bound, it is a poorly written function.

It looks to me like you're thinking that writing A: App should mean that the T generic parameter is unconstrained, still acting as a variable. That is not how default generic parameters work. trait App<T = ()> doesn't mean “use () if the type is not specified or inferred to be something else”, but merely “use () if the type is not specified, at each place this trait is mentioned”. Therefore, writing fn start<A: App + 'static> is exactly the same as writing fn start<A: App<()> + 'static>.

Your original code will compile if start() is made generic over the T parameter of App:

pub fn start<T: 'static, A: App<T> + 'static>(app: A) {

(One possible source of confusion here is that with associated types in a trait bound, leaving them out means lack of constraint, and they also use = syntax. But these two uses of = are not really similar at all.)

1 Like