16

I've encountered a problem which I reduced to the following code:

trait Logger {}

struct DefaultLogger;

impl Logger for DefaultLogger {}

struct A<L> where L: Logger {
    logger: Box<L>,
}

impl<L> A<L> where L: Logger {
    fn new() -> Self {
        let logger = DefaultLogger;

        A {
            logger: Box::new(logger),
                          // ^^^^^^ Here is the problem
        }
    }
}

fn main() {
    let a = A::new();
}

which produces the following error:

error[E0308]: mismatched types
  --> src/main.rs:16:30
   |
16 |             logger: Box::new(logger),
   |                              ^^^^^^ expected type parameter, found struct `DefaultLogger`
   |
   = note: expected type `L`
              found type `DefaultLogger`

When I construct trait A in a normal function (like main), like I expected it to work. Example:

trait Logger {}

struct DefaultLogger;

impl Logger for DefaultLogger {}

struct A<L> where L: Logger {
    logger: Box<L>,
}

fn main() {
        let logger = DefaultLogger;

        let _a = A {
            logger: Box::new(logger),
        };
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Martin Fink
  • 1,746
  • 3
  • 16
  • 32

1 Answers1

17

The problem is here:

impl<L> A<L> where L: Logger {
    fn new() -> Self {
        let logger = DefaultLogger;

        A {
            logger: Box::new(logger),
        }
    }
}

You want to return A<L> but you return A<DefaultLogger> where you cannot be sure that L == DefaultLogger.

To fix that you can provide specialisation that will create method A::new only for DefaultLogger:

impl A<DefaultLogger> {
    fn new() -> Self {
        let logger = DefaultLogger;

        A {
            logger: Box::new(logger),
        }
    }
}
Hauleth
  • 22,873
  • 4
  • 61
  • 112
  • If I were to pass the logger as a parameter, would that work too? Then I wouldn't have to create a new impl block for every subtype of `Logger`. – Martin Fink Jul 22 '17 at 11:41
  • 5
    This answer saved me so much time debugging and and greatly enhanced my understanding of how generics work in Rust. I know "thank you" comments aren't allowed on SO, but thank you anyway. – Jonah Jul 30 '19 at 14:48