Why todo!() doesn't work in the way i thought?

why todo!() cannot make it compile?

fn read_conf<P: AsRef<Path>>(path: P) -> impl conf::Conf {
    todo!();
}

it shows:

error[E0277]: the trait bound `(): Conf` is not satisfied                                                                            
 --> bin\client\src\main.rs:8:42
  |
8 | fn read_conf<P: AsRef<Path>>(path: P) -> impl conf::Conf {
  |                                          ^^^^^^^^^^^^^^^ the trait `Conf` is not implemented for `()`
  |
  = help: the trait `Conf` is implemented for `ClientConf`

1 Like

this error is confusing, due to the never type ! coersion will fallback to ()

https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html

for now, you can use a placeholder type as a workaround:

fn read_conf<P: AsRef<Path>>(path: P) -> impl conf::Conf {
    // suppose you already have a type that implements the trait,
    // just use it as a placeholder as the hidden return type
    // xxxx if you don't have any type available yet, but the trait is `dyn` compatible,
    // xxxx you can use a trait object as the place holder
    let ret: SomeType = todo!();
    // xxxx let ret: &'static dyn conf::Conf = todo!();
    ret
}

CORRECTION:

&dyn Trait does NOT implement Trait by default, while dyn Trait is unsized. so trait objects cannot be used. see comment by @Bruecki below.

2 Likes
  1. You have a semicolon after todo!(), so even on edition 2024 (which changes the fallback behavior) you're trying to return ()

  2. Next note that -> impl Trait is an opaque alias to some other type inferred by the function body, not its own type, and that there's no way to infer a type other than ! if you only "return" a diverging expression like todo!()

  3. And also that while ! has magical coercion powers, it's also a type that only implements a few select traits itself, not all traits[1]

  4. So to make diverging expressions like todo!() work with -> impl Trait, you have to cast them to some known implementor, or have some other return path with a concrete type being returned that ! will coerce to, etc.

I.e. you'll need the placeholder type indefinitely, not just as workaround until edition 2024.


  1. which would have to be very magical and self-contradictory compared to any other type, e.g. being both Copy and Drop ↩ī¸Ž

7 Likes

Note that &dyn Trait does not implement Trait by default. Sadly, you cannot return dyn Trait because it is unsized.

For any type that implements Conf you can alternatively use a direct cast instead of coercion like this

todo!() as SomeTypeThatImplConf

to get a one-liner.

3 Likes