How to fix this 'expected lifetime parameter' build error?


#1

I have a build error for the following code (just for simply demo this error) (you can find it on playground: https://play.rust-lang.org/?gist=1dd9e83c1baf675e759bfe4b79e01821&version=stable&mode=debug&edition=2015 )

    struct Difficulty<'a> {
    	store: Option<u64>,
    	batch: Option<&'a str>,
    }
    impl<'a> Difficulty<'a> {
    	pub fn from(store: u64) -> Difficulty {
    		Difficulty {
    			store: Some(store),
    			batch: None,
    		}
    	}
    	
        pub fn from_batch(batch: &str) -> Difficulty {
            Difficulty {
            	store: None,
            	batch: Some(batch),
            }
        }
    }

    fn main() {
        println!("Hello, world!");
        
        let diff1 = Difficulty::from(1);
        println!("diff1 = {}", diff1.store.unwrap());

        let str2 = "batch test";
        let diff2 = Difficulty::from_batch(&str2);
        println!("diff2 = {}", diff2.batch.unwrap());    
    }

The build error is:

error[E0106]: missing lifetime specifier
 --> src/main.rs:7:29
  |
7 |     pub fn from(store: u64) -> Difficulty {
  |                                ^^^^^^^^^^ expected lifetime parameter
  |
  = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments

But the problem is the structure have 2 elements, one is lifetime parameter but another is not. How can I fix the build error but allow a element without lifetime?

Thanks if anybody can give me a help!


#2

Please anybody help? I know a fix solution is to split this structure into 2 and duplicate these code, but it seems not clean and a little bit ugly.

Is there a fix solution on this error?


#3

It doesn’t matter whether some elements of the structure doesn’t have lifetime (as long as this is valid definition, of course). If any of them has it (non-static), the whole structure would also have it, and you’ve defined it as such: struct Difficulty<'a> { ... }. Now, anywhere it is used, the lifetime must be either elided or specified explicitly.

The function that doesn’t receive any reference parameters can’t be subject to lifetime elision (compiler has no way of telling how long the return value is allowed to live). It seems that in this case you won’t be using any &str value at all, so it’s possible to mark output parameter as Difficulty<'static>.


#4

Thanks @Cerberuser for your help :bouquet:

I can’t make it as 'static, these code is used to demo this lifetime build problem, the real situation is much more complex and both from() function will be called frequently.

So, perhaps the only way to fix this is to split into 2 structures :frowning:


#5

Well, so it means that you could do let diff = Difficulty::from(number) and then set diff.batch in some other way, right? Then you must guarantee that your Difficulty struct won’t live too long. Since Difficulty::from has no ways of figuring it by itself, it seems that the function itself must be generic:

impl<'a> Difficulty<'a> {
	pub fn from<'b>(store: u64) -> Difficulty<'b> {
		Difficulty {
			store: Some(store),
			batch: None,
		}
	}
}

#6

@Cerberuser Yay! it works! :clap: :bouquet: :bouquet: :bouquet:

https://play.rust-lang.org/?gist=0ab0751695e9ae47bed4f3fe50aa3723&version=stable&mode=debug&edition=2015

How it works? why lifetime b can be used as returned structure but structure itself declared lifetime a ?


#7

These are two independent parameters - one for the whole struct, another for the function (note that function doesn’t receive any argument of Difficulty type, so it doesn’t have to depend on its lifetime). When we declare type in fn from<'b> as Difficulty<'b>, it means “take any lifetime parameter passed to this function (let it be 'static by default) and pass it to the Difficulty as its parameter, however it does call it”.