Reputation: 43052
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.
Upvotes: 4
Views: 5494
Reputation: 14041
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.
Upvotes: 2
Reputation: 430851
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.
Upvotes: 4