Enum and lifetime

Hello there!

How to get this code compile?

 pub enum Foo<'a, Operation> {
     A0 { str: String, data: &'a Operation },
     A1 { str: String },
 }
 
 fn main() {
     let arr= vec![0, 1, 2];
     let a0 = Foo::A0{str: "arr".to_owned(), data: &arr};
     let a1 = Foo::<'_, ()>::A1{str: "no_arr".to_owned()};
 }

Got error:

error[E0109]: lifetime arguments are not allowed for this type
  --> main\src\main.rs:76:29
   |
76 |     let a1 = Foo::<'_, ()>::A1{str: "no_arr".to_owned()};
   |                             ^^ lifetime argument not allowed

Ok, lifetime not allowed:

pub enum Foo<'a, Operation> {
    A0 { str: String, data: &'a Operation },
    A1 { str: String },
}

fn main() {
    let arr= vec![0, 1, 2];
    let a0 = Foo::A0{str: "arr".to_owned(), data: &arr};
    let a1 = Foo::A1{str: "no_arr".to_owned()};
}

Got error:

error[E0282]: type annotations needed for `Foo<'_, Operation>`
  --> main\src\main.rs:76:14
   |
76 |     let a1 = Foo::A1{str: "no_arr".to_owned()};
   |         --   ^^^^^^^ cannot infer type for type parameter `Operation` declared on the enum `Foo`
   |         |
   |         consider giving `a1` the explicit type `Foo<'_, Operation>`, where the type parameter `Operation` is specified

But if we use tuples everything fine:

 pub enum Foo<'a, Operation> {
     A0(String, &'a Operation),
     A1(String),
 }
 
 fn main() {
     let arr= vec![0, 1, 2];
     let a0 = Foo::A0("arr".to_owned(), &arr);
     let a1 = Foo::<'_, ()>::A1("no_arr".to_owned());
 }
1 Like

Workaround:

pub enum Foo<'a, Operation> {
    A0 { str: String, data: &'a Operation },
    A1 { str: String },
}

type Bar<'a> = Foo<'a, ()>;

fn main() {
    let arr= vec![0, 1, 2];
    let a0 = Foo::A0{str: "arr".to_owned(), data: &arr};
    let a1 = Bar::<'_>::A1{str: "no_arr".to_owned()};
}

So yeah, the type of the enum is Foo<'a, Operation>, even for a variant that uses neither the type nor the reference.

Same thing can happen when you do

let option = None;

Generally the compiler will infer the type from how you later use the variable, but if there is no use, especially while you are still writing the code, it will complain that it doesn't know what type option is as Option<T> is generic.

So in your case the actual type does matter if you want to put your Foo in a vector of Foo, because they will all have to be the same type. For now I would write:

let a1: Foo<()> = Foo::A1{str: "no_arr".to_owned()};

I find it slightly more convenient than the turbofish.

But if you later use it in a place that allows the compiler to infer the type, you won't need the type annotation anymore: Playground. Note that in the playground usize also causes type inference, as the integers in the Vec could have been some other type. Note quite sure why the compiler doesn't complain about those. Apparently there is a default for integers: i32... and for floats: f64.

But I can write this:

let option = Option::<()>::None;

And my workaround working fine.

FWIW, you can even write None::<()> :slightly_smiling_face:

It does work with Foo::A1::<'_, ()> (playground), so perhaps it's a compiler bug.

You never write lifetime annotations in constructors. This would work :

Foo::A1::<()> {
    str: "no_arr".to_owned(),
}