Передача переменных в функцию и время жизни

Я хочу передать HashMap и элемент - Foo моей функции. Я не понимаю время жизни переменных. Пожалуйста помоги.

Результат: ошибка [E0623]: несоответствие времени жизни

use std::collections::HashMap;

#[derive(Debug)]
struct Foo<'a> {
    x: &'a i32,
}

fn add_to_map<'l>(map: &'l HashMap<u32, &'l Foo>,  element : &'l Foo) 
{
    map.insert(1, element);
}

fn main()
{
    let mut map: HashMap<u32, &Foo> = HashMap::new();

    let v = 5;
    let mut f = Foo { x: &v };
    add_to_map(&map, &f);
    println!("now f is {:?}", f);
}

Вывод компилятора:

error[E0623]: lifetime mismatch
  --> src/main.rs:11:19
   |
9  | fn add_to_map<'l>(map: &'l HashMap<u32, &'l Foo>,  element : &'l Foo) 
   |                                             ---                  --- these two types are declared with different lifetimes...
10 | {
11 |     map.insert(1, element);
   |                   ^^^^^^^ ...but data from `element` flows into `map` here

error: aborting due to previous error

person Michał Hanusek    schedule 16.10.2019    source источник


Ответы (1)


9  | fn add_to_map<'l>(map: &'l HashMap<u32, &'l Foo>,  element : &'l Foo) 
   |                                             ---                  --- these two types are declared with different lifetimes...
10

Foo<'a> появляется здесь дважды ('a - время жизни x внутри Foo). Поскольку вы не указали явные значения для времени жизни 'a, компилятор будет предполагать, что Foo как значение HashMap и element Foo будут иметь разные времена жизни 'a. Этот подход в целом работает хорошо.

Однако вы действительно хотите вставить element в map, что требует совпадения сроков жизни. Компилятор отклоняет ваш код, поскольку время жизни не совпадает. Вы должны четко указать время жизни x внутри Foo, например,

// Verbose.
fn add_to_map<'l>(map: &'l mut HashMap<u32, &'l Foo<'l>>,  element : &'l Foo<'l>) 

Вам не нужно указывать явное время жизни самого map, поскольку компилятор может проверить на вызывающем сайте, что map живет по крайней мере столько же, сколько element,

// Slightly less verbose.
fn add_to_map<'l>(map: &mut HashMap<u32, &'l Foo<'l>>,  element : &'l Foo<'l>) 

Обратите внимание, что в любом случае map должно быть mut в add_to_map.

person Benjamin Bannier    schedule 16.10.2019
comment
Ok. Это работает. Но когда я распечатываю содержимое карты, у меня возникает ошибка. play.rust-lang.org/ - person Michał Hanusek; 16.10.2019
comment
Непосредственная проблема заключается в том, что add_to_map заимствует map, поэтому использование его в той же области может быть нецелесообразным. Я не уверен, какую именно проблему вы пытаетесь решить, но add_to_map мне кажется странным; если бы вы могли, например, просто вставить в такое место, как main, вы бы не столкнулись с этой проблемой (вы можете делегировать вычисление значения помощнику). Но даже при этом HashMap, который у вас здесь, не может быть перемещен, что затрудняет использование (аналогичный код был бы возможен на таких языках, как C или C ++, но также было бы очень легко вводить ошибки таким образом). - person Benjamin Bannier; 16.10.2019
comment
Ok. Как это сделать? Прошу привести пример. Может мне стоит вернуть из функции новый HashMap? - person Michał Hanusek; 16.10.2019
comment
Просто вставить на место? - person Benjamin Bannier; 16.10.2019
comment
Я не понимаю. Пожалуйста, посмотрите и поправьте мой пример. play.rust-lang.org/ - person Michał Hanusek; 16.10.2019
comment
Я до сих пор не знаю, какой реальный вариант использования выходит за рамки эксперимента с продолжительностью жизни, но я имел в виду следующее: play.rust-lang.org/. - person Benjamin Bannier; 16.10.2019
comment
Я хочу изменить карту (добавить элемент) и распечатать карту (на следующем шаге). Я решил это так: play.rust-lang.org/ Вопрос: можно ли это решить в стиле C ++ (передав ref)? - person Michał Hanusek; 16.10.2019
comment
Нет, я не думаю, что это можно решить в стиле C ++. Уже в C ++ неясно, хранит ли add_to_map map где-то еще, поэтому нужно будет оставить исходный map на месте. Одним из способов решения этой проблемы в C ++ являются комментарии, которые не проверяются компилятором. Можно также закодировать это в подписи, потребовав, чтобы map передавалось как некопируемое значение (например, unique_pt<map>), чтобы сигнализировать о передаче права собственности, а затем повторно заполнить исходное значение, вернув аналогичное значение из функции. Отличие от Rust в том, что небезопасная передача по ссылке является незаконной. - person Benjamin Bannier; 16.10.2019