rusttraitslifetime-scoping

How to express lifetime bounds in Rust when using references in trait methods


Here is a code snippet illustrating a lifetimes issue I ran into:

use std::collections::HashSet;

pub trait Store {
    fn put<'a, 'b: 'a>(&mut self, val: &'b str);
}


struct Hstore<'a> {
    hset: HashSet<&'a str>,
}

impl<'a> Store for Hstore<'a> {
    fn put<'b, 'c: 'b>(&mut self, val: &'c str) {
        self.hset.insert(val);
    }
}

My intention is to store referenced values passed by the trait in the struct as long as their lifetimes outlives that of the struct.

A get a compilation error:

error: lifetime may not live long enough
  --> src/lib.rs:14:9
   |
12 | impl<'a> Store for Hstore<'a> {
   |      -- lifetime `'a` defined here
13 |     fn put<'b, 'c: 'b>(&mut self, val: &'c str) {
   |                -- lifetime `'c` defined here
14 |         self.hset.insert(val);
   |         ^^^^^^^^^^^^^^^^^^^^^ argument requires that `'c` must outlive `'a`
   |
   = help: consider adding the following bound: `'c: 'a`

How do I add this 'c: 'a bound? I tried several approaches which did not work.

Also why can't the compile infer the life 'b in the implementation has the same lifetime validity as 'a.


Solution

  • You can make your trait generic over the lifetime.

    pub trait Store<'a> {
        fn put(&mut self, val: &'a str);
    }
    

    Then you can easily implement it.

    impl<'a> Store<'a> for Hstore<'a> {
        fn put(&mut self, val: &'a str) {
            self.hset.insert(val);
        }
    }
    

    This isn't very common, but should work fine. The standard library trait Pattern is very similar, since it also has a method that incorporates a string slice into a type that escapes the function.

    An alternative is to make the trait fully generic.

    pub trait Store<T> {
        fn put(&mut self, val: T);
    }
    
    impl<'a> Store<&'a str> for Hstore<'a> {
        fn put(&mut self, val: &'a str) {
            self.hset.insert(val);
        }
    }