George
George

Reputation: 2801

Lifetime issue with From<&V> trait constraint

The following code produces the lifetime errors below despite the fact that the V instance in question is owned.

use std::collections::hash_map::HashMap;
use std::cmp::Eq;
use std::hash::Hash;

trait Set<V> {
    fn set(&mut self, value: V) -> Option<V>;
}

impl<'a, K: Eq + Hash + From<&'a V>, V: 'a> Set<V> for HashMap<K, V> {
    fn set(&mut self, v: V) -> Option<V> {
        let k = K::from(&v);
        self.insert(k, v)
    }
}

The resulting errors ...

   |
9  | impl<'a, K: Eq + Hash + From<&'a V>, V: 'a> Set<V> for HashMap<K, V> {
   |      -- lifetime `'a` defined here
10 |     fn set(&mut self, v: V) -> Option<V> {
11 |         let k = K::from(&v);
   |                 --------^^-
   |                 |       |
   |                 |       borrowed value does not live long enough
   |                 argument requires that `v` is borrowed for `'a`
12 |         self.insert(k, v)
13 |     }
   |     - `v` dropped here while still borrowed

error[E0505]: cannot move out of `v` because it is borrowed
  --> src/lib.rs:12:24
   |
9  | impl<'a, K: Eq + Hash + From<&'a V>, V: 'a> Set<V> for HashMap<K, V> {
   |      -- lifetime `'a` defined here
10 |     fn set(&mut self, v: V) -> Option<V> {
11 |         let k = K::from(&v);
   |                 -----------
   |                 |       |
   |                 |       borrow of `v` occurs here
   |                 argument requires that `v` is borrowed for `'a`
12 |         self.insert(k, v)
   |                        ^ move out of `v` occurs here

Upvotes: 6

Views: 100

Answers (1)

kmdreko
kmdreko

Reputation: 59952

Use a higher-rank trait bound denoted by for<'a>:

impl<K: Eq + Hash + for<'a> From<&'a V>, V> Set<V> for HashMap<K, V> {
    fn set(&mut self, v: V) -> Option<V> {
        self.insert(K::from(&v), v)
    }
}

See it working on the playground.

As a normal generic parameter, the lifetime 'a is determined by the caller, which would outlive the set() call itself. However, the life of v is only local to the function body, meaning any lifetime 'a used to call K::from() can outlive the content that may be contained in V. Just because v is an owned value doesn't mean it doesn't have lifetimes associated with it, it is generic after all. The compiler is doing its best, but its suggestion is not what you want.

Using a for<'a> From<&'a V> constraint means that the K::from() call will work for any lifetime, including the short function-local lifetime of v.

Upvotes: 5

Related Questions