You’re talking about an associated type here, not a type alias - just wanted to make sure you have the right terminology in place in case you want to do some additional research.
In all likelihood, you can get away with:
type Result = Result<User, Error<'static>>;
Putting things like lazy_static aside, this basically means you’ll know, at compile time, the name of the missing field to use for the MissingField variant (if you ever construct it).
Note also that the Message trait requires that Result: 'static. This means you have no choice but to use a static lifetime if your associated type has a lifetime parameter.
But, all that said, you likely just want to make MissingField(String) - ie replace the reference with an owned string and then drop the lifetime parameter from Error. Did you have a reason for wanting a string slice?
Separately, you probably want #[fail(display = "Missing field: {}", _0)] - this will include the actual field value in the message and makes for better debugging/display experience.
You’re talking about an associated type here, not a type alias, Thank you for correcting me.
Did you have a reason for wanting a string slice?
I was assuming that none 'static lifetime variants will be disposed somewhere when I didn't need them anytime (impl Drop trait). This is just my guessing based on Java and Go (GC language)
I haven't learnt Rust memory model
Maybe for modern computers, these tiny strings or objects won't make program inefficient anymore
I’d suggest starting your Rust foray with reading the free book: Foreword - The Rust Programming Language. There’s a section dedicated to ownership, but read the whole book without skipping anything - it won’t make you a Rust expert but will make the learning experience quite a bit smoother.
On a side note, I'd also like to suggest to not use &'a str as the field of Error::MissingField.
I've found that the thing you most often want to do with an error is bubble it up. However, lifetimes put a heavy restriction on that: you can't bubble an error past the scope of the variable that you're borrowing the field name from!
Since errors often are something rare and performance isn't a concern when handling them, it's IMO easier to just use a String here. An additional bonus is that you can avoid dealing with lifetimes in your case. Your type will simply be:
Note that it is possible to have a type alias with a lifetime parameter:
type Result<'a> = std::result::Result<User, Error<'a>>;
Note that you have to declare the lifetime parameter before using it. You also must disambiguate the Result identifier because otherwise it will be a cyclic definition. While you can do stuff like let x = x.unwrap(); in variable declarations, it doesn't work with type alias declarations.
While type aliases and associated types both use the type keyword, they're not quite the same thing. If you see a type directly inside an impl block, that's an associated type. Defining type parameters/lifetimes on associated types isn't a stable feature yet, hence the error you're getting - that said, I don't think that's actually what you're trying to do here!
To make things a little clearer, let's boil things down to the minimum possible example:
trait Trait {
type AssocType;
}
struct Struct;
struct StructWithLifetime<'a>;
impl Trait for Struct {
type AssocType = StructWithLifetime<'a>;
}
If you try to run this code, you'll get an error saying the lifetime is in impl Trait for Struct is undefined - in the context of that implementation, the compiler does not know what lifetime 'a is. So we add a lifetime definition:
impl<'a> Trait for Struct {
type AssocType = StructWithLifetime<'a>;
}
Now we get a slightly different error - it says that 'a is an 'unconstrained' lifetime. The compiler knows that 'a is the lifetime of StructWithLifetime, but it has nothing to compare that to, so it can't determine exactly how long that struct needs to live.
So we need to add another lifetime annotation to the implementation block - but where? We can't put it on Trait, because Trait isn't defined as having any lifetime parameters in its original definition. The same goes for Struct. If generic associated types were stable andTrait defined AssocType as having a lifetime, then we could put it there, but it doesn't, so we can't.
That's the real issue with your original code - there are no lifetime parameters on any of the other elements of the trait implementation (Message, User and Result) that you can tie 'a to, so the compiler has no context with which to figure out how long 'a actually is. The only lifetime you can feasibly use is 'static, which will always be the lifetime of the whole program, no matter what the context. That's a pretty big limitation, hence why people are recommending not to use lifetimes here