Lifetime issue with From<&V> trait constraint
Asked Answered
K

1

6

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
Kesha answered 23/12, 2021 at 7:40 Comment(2)
It is not clear what you're trying to achieve. The compiler's error message is pretty clear, and also offers a suggestion.Downbow
I have tried various permutations including the suggestion from the compiler. However, the suggestion does not work but does result in a lot more error messages. As a result, I chose to publish this version as oppose to that one. In any event, I will update it.Kesha
N
5

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.

Neoptolemus answered 23/12, 2021 at 8:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.