4

I have a

struct Foo<T>
where
    T: // ... some complex trait bound ...
{
    a: Bar,
    b: Option<T>,
}

When attempting to instantiate the struct with a b: None the compiler complains that it cannot infer the type and requires a type hint e.g. via the turbofish syntax. That is onerous on the caller because they will have to find a type that fulfills the trait bounds and import it despite not caring about that optional functionality.

I think what I am looking for would be a bottom type that automatically fulfills any trait bounds but cannot be instantiated so that None::<Bottom> could be used, but I have not found such a type in the documentation.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
the8472
  • 40,999
  • 5
  • 70
  • 122
  • 1
    The bottom type is called `!` in Rust. Does it work for you? – Matthieu M. Feb 09 '17 at 15:54
  • Which part are you suggesting is onerous? If it's the turbofish aspect, how do you suggest the bottom type be specified other than that? – Shepmaster Feb 09 '17 at 15:56
  • @MatthieuM. No, it does not match the trait bound. – the8472 Feb 09 '17 at 15:58
  • @Shepmaster `::<!>` would be ok but it doesn't work anyway, `::` is not. Note that those are trait bounds, so the API user would have to figure out a possible candidate type (that he doesn't even intend to instantiate!) just to pass `None` – the8472 Feb 09 '17 at 16:01

2 Answers2

4

There's a feature in the works that allows specifying the never type as !. This is not present in stable Rust, so you need to use a nightly and a feature flag:

#![feature(never_type)]

fn thing<T>() -> Option<T> {
    None
}

fn main() {
    thing::<!>();
}

However, this doesn't work for your case yet (this is part of the reason that it's unstable):

#![feature(never_type)]

trait NothingImplementsMe {}

fn thing<T>() -> Option<T> 
    where T: NothingImplementsMe,
{
    None
}

fn main() {
    thing::<!>();
}
error[E0277]: the trait bound `!: NothingImplementsMe` is not satisfied
  --> src/main.rs:12:5
   |
12 |     thing::<!>();
   |     ^^^^^^^^^^ the trait `NothingImplementsMe` is not implemented for `!`
   |
   = note: required by `thing`

The very first unresolved question on the tracking issue is:

What traits should we implement for !?


Since this feature is both unstable and doesn't do what you want, you may want to consider creating your own bespoke "bottom" type:

trait AlmostNothingImplementsMe {
    fn foo();
}

struct Nope;
impl AlmostNothingImplementsMe for Nope {
    fn foo() { unimplemented!() }
}

fn thing<T>() -> Option<T> 
    where T: AlmostNothingImplementsMe,
{
    None
}

fn main() {
    thing::<Nope>();
}

To improve the UX of this, I'd suggest creating a builder of some type that starts you off with the faux-bottom type:

mod nested {
    pub trait AlmostNothingImplementsMe {
        fn foo();
    }

    pub struct Nope;
    impl AlmostNothingImplementsMe for Nope {
        fn foo() { unimplemented!() }
    }

    pub fn with_value<T>(t: T) -> Option<T> 
        where T: AlmostNothingImplementsMe,
    {
        Some(t)
    }

    pub fn without_value() -> Option<Nope> {
        None
    }
}

fn main() {
    nested::without_value();
}

You can see this similar pattern in crates like Hyper, although it boxes the concrete type so you don't see it from the outside.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    @trentcl: It's a work in progress, there are many things that don't work with uninhabited types that one would expect to work. – Matthieu M. Feb 09 '17 at 16:01
  • Sorry for deleting my comment and making you look like talking to yourself! For others, what I asked (before Shepmaster edited his answer to include the information) was "But does `!` implement any trait bound?" – trent Feb 09 '17 at 16:28
2

One option to avoid the need to the turbofish operator is to have a type alias:

trait MyTrait {}
impl MyTrait for () {}

struct Foo<T: MyTrait> {
    i: isize,
    o: Option<T>,
}

type Bar = Foo<()>;

fn main() {
    let foo_default = Bar { i: 1, o: None };
}

I used () as the default for simplicity, but ! (when available) or your own bottom type as in @Shepmaster's answer may be better.

A constructor function could also work if you don't mind Foo::new_default(i) or similar.

Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
  • `()` does not fulfill trait bounds and they cannot be implemented when the traits come from external crates – the8472 Feb 09 '17 at 16:15
  • 2
    You haven't said what the bounds are; I am suggesting implementing them (in some reasonable default way, possibly involving `unreachable!()` or `assert!()`) for some kind of default type (`()` as an example) and using that as the default. – Chris Emerson Feb 09 '17 at 16:18
  • But it is not a general solution since it assumes that all traits are crate-local. – the8472 Feb 09 '17 at 16:21
  • 2
    True it's not general; I was just offering an option which might work in some similar cases. As I mentioned a custom type could be substituted instead of `()` which would resolve the non-local trait issue. – Chris Emerson Feb 09 '17 at 16:26