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

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.