fn main() {
println!("{:?}", Greeting::Hi("How you doing".to_string()));
}
But if I try to use the Bye variant like this
fn main() {
println!("{:?}", Greeting::Bye);
}
I get the error
Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed
--> src/main.rs:10:22
|
10 | println!("{:?}", Greeting::Bye);
| ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the enum `Greeting`
|
help: consider specifying the generic argument
|
10 | println!("{:?}", Greeting::<T>::Bye);
| +++++
For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground` (bin "playground") due to previous error
And to make it work, I would have to use it like this
Having to manually specify the ::<String>:: part makes me sad
Is there anyway I can make this work, without specifying the ::<String>:: part? I was of the opinion that specifying the default to String in the definition, like this enum Greeting<T = String> should have helped...but that apparently is not the case..
I don't think there is a way to achieve this with the enum as it is, since the Debug implementation could depend on the generic type.
Edit: the implementation does actually depend on the generic type since the debug function is defined on the type Greetings, not on the individual variants Bye and Hi.
Type defaults are considered when completely elided in code location that expects a type, but inference ignores type defaults in expressions. Here's a summary.
So basically no way to do want I want? Have a enum with a generic type that can be used with one of it's variants, without the verbosity of having to explicitly setting the type?
The example shows how to do that using a default type parameter.
If you mean is there a way to do it without the default type parameter / is there a way to have a variant around with the generic of the enum "unset", the answer is no. Rust is statically typed and the type of a variant is the type of the enum. The type parameter must be specified or inferred in some way.
You could have two different enums and a From implementation or such.
If you have a generic type and don't care about what the generic part actually is for whatever reason, there will eventually be the never (!) type. But in the meantime, you can build something similar with an enum with no options, like:
#[derive(Debug)]
enum Never {}
so your Option-like Greeting enum could be instantiated with:
let bye: Greeting<Never> = Greeting::Bye;
(and that's the only way to instantiate that particular Greeting, since there's no way to build the Hi option).
There's a very concrete reason why it's not possible: in your case the size of Greeting<T> is the size of T + one byte for the variant - excepted if T has a niche then Greeting<T>'s size will be the same as T's.
So the compiler wouldn't be able to compute the size of Greeting::Bye without knowing what a T is. And you can't put something on the stack if you don't know its size at compile-time (alloca() be damned).
Just an idea because no one suggested it.
If your question is about ergonomics of using the type when you don't care about what T is, you could use a type alias:
type Greetings = Greeting<String>;
fn main() {
let hello = Greetings::Hi("hi".into());
let bye = Greetings::Bye;
}