Как в замыкании обрабатывать ошибку на примере кода?

Как правильно поправить код, чтобы уйти от ошибки:
'a', 'b' -- is borrowed here
returns a value referencing data owned by the current function?

use std::collections::HashMap;

#[derive(Debug)]
struct Error;

fn f1(s: &str) -> Result<&str, Error> { 
   // something for test:
   if s.len() > 5 {
      Ok(s)
   }else{
      Err(Error)
   }      
}

fn f2(s: &str) -> Result<&str, Error> { 
   todo!() 
}



fn r(raw: HashMap<String, String>) -> Result<HashMap<&'static str, &'static str>, Error> {
    raw
        .into_iter()
        .map(|(a, b)| {
            
            // println!("{:?} - {:?}", a, b);
            Ok((
                f1(&a)?,
                f2(&b)?,
            ))
        })
        .collect()
}



fn main() {
    let raw: HashMap<String, String> = HashMap::from([
       ("Mercury".to_string(), "0.4".to_string()),
       ("Venus".to_string(), "0.7".to_string()),
       ("Earth".to_string(), "1.0".to_string()),
       ("Mars".to_string(), "1.5".to_string()),
    ]);
    
    println!("{:?}", r(raw));
}

Ответы (1 шт):

Автор решения: extrn

Если компилятор предлагает использовать лайфтайм 'static, значит велика вероятность, что вы делаете что-то неправильно, а он просто не нашел лучшего предложения по исправлению ошибки.

Здесь ситуация во многом схожая с вот такой, достаточно очевидной ошибкой

fn f(x: i32) -> &i32 {
    &x
}

Даже не считая того, что мы пытаемся вернуть ссылку на локальную переменную, проблема возникает в выводе времени жизни результата в сигнатуре функции. Если бы аргумент функции так же был бы передан по ссылке, то, согласно правилам lifetime elision, компилятор использовал бы время жизни этой ссылки и для результата. В данном же случае требуется указать время жизни явно, и единственный известный ему подходящий вариант – 'static, но он честно признается, что вряд ли это то, что вам нужно.

help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
  |
1 | fn f(x: i32) -> &'static i32 {

Правда, для этого простого примера он также предложит более логичный вариант

help: instead, you are more likely to want to change the argument to be borrowed...
  |
1 | fn f(x: &i32) -> &i32 {

даже два

help: ...or alternatively, you might want to return an owned value
  |
1 - fn f(x: i32) -> &i32 {
1 + fn f(x: i32) -> i32 {

И даже в более сложных случаях

fn f(x: Option<i32>) -> Option<&i32> {
    x.as_ref()
}

сможет предложить адекватное решение

1 | fn f(x: &Option<i32>) -> Option<&i32> {
  |         +

Но он тоже не всесилен, и здесь уже ограничится только предложением со 'static

fn f(x: i32) -> (&i32, &i32) {
    (&x, &x)
}

Дублирую исправленный код из комментария к вопросу

use std::collections::HashMap;

#[derive(Debug)]
struct Error;

fn f1(s: &str) -> Result<&str, Error> { 
   // for test:
   if s.len() > 5 {
      Ok(s)
   }else{
      Err(Error)
   }      
}

fn f2(s: &str) -> Result<&str, Error> { 
   todo!() 
}

fn r(raw: &HashMap<String, String>) -> Result<HashMap<&str, &str>, Error> {
    raw
        .into_iter()
        .map(|(a, b)| {
            
            // println!("{:?} - {:?}", a, b);
            Ok((
                f1(&a)?,
                f2(&b)?,
            ))
        })
        .collect()
}

fn main() {
    let raw: HashMap<String, String> = HashMap::from([
       ("Mercury".to_string(), "0.4".to_string()),
       ("Venus".to_string(), "0.7".to_string()),
       ("Earth".to_string(), "1.0".to_string()),
       ("Mars".to_string(), "1.5".to_string()),
    ]);
    
    println!("{:?}", r(&raw));
}
→ Ссылка