Как мне взять ссылку на то, что находится внутри Option <T>?

Как вытащить ссылку из Option и передать ее с определенной продолжительностью жизни вызывающего?

В частности, я хочу взять ссылку на Box<Foo> из Bar, в которой есть Option<Box<Foo>>. Я думал, что смогу сделать:

impl Bar {
    fn borrow(&mut self) -> Result<&Box<Foo>, BarErr> {
        match self.data {
            Some(e) => Ok(&e),
            None => Err(BarErr::Nope),
        }
    }
}

... но это приводит к:

error: `e` does not live long enough
  --> src/main.rs:17:28
   |
17 |             Some(e) => Ok(&e),
   |                            ^ does not live long enough
18 |             None => Err(BarErr::Nope),
19 |         }
   |         - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:54...
  --> src/main.rs:15:55
   |
15 |       fn borrow(&mut self) -> Result<&Box<Foo>, BarErr> {
   |  _______________________________________________________^ starting here...
16 | |         match self.data {
17 | |             Some(e) => Ok(&e),
18 | |             None => Err(BarErr::Nope),
19 | |         }
20 | |     }
   | |_____^ ...ending here

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:16:15
   |
16 |         match self.data {
   |               ^^^^ cannot move out of borrowed content
17 |             Some(e) => Ok(&e),
   |                  - hint: to prevent move, use `ref e` or `ref mut e`

Hm, ok. Возможно, нет. Смутно выглядит то, что я хочу сделать, связано с Option::as_ref, например, я мог бы сделать:

impl Bar {
    fn borrow(&mut self) -> Result<&Box<Foo>, BarErr> {
        match self.data {
            Some(e) => Ok(self.data.as_ref()),
            None => Err(BarErr::Nope),
        }
    }
}

... но это тоже не работает.

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

#[derive(Debug)]
struct Foo;

#[derive(Debug)]
struct Bar {
    data: Option<Box<Foo>>,
}

#[derive(Debug)]
enum BarErr {
    Nope,
}

impl Bar {
    fn borrow(&mut self) -> Result<&Box<Foo>, BarErr> {
        match self.data {
            Some(e) => Ok(&e),
            None => Err(BarErr::Nope),
        }
    }
}

#[test]
fn test_create_indirect() {
    let mut x = Bar { data: Some(Box::new(Foo)) };
    let mut x2 = Bar { data: None };
    {
        let y = x.borrow();
        println!("{:?}", y);
    }
    {
        let z = x2.borrow();
        println!("{:?}", z);
    }
}

Я уверен, что то, что я пытаюсь сделать, действительно здесь.

Ответ 1

Прежде всего, вам не нужно &mut self.

При сопоставлении вам следует сопоставить e как ссылку. Вы пытаетесь вернуть ссылку e, но время ее жизни только для этого оператора соответствия.

enum BarErr {
    Nope
}

struct Foo;

struct Bar {
    data: Option<Box<Foo>>
}

impl Bar {
    fn borrow(&self) -> Result<&Foo, BarErr> {
        match self.data {
            Some(ref x) => Ok(x),
            None => Err(BarErr::Nope)
        }
    }
}

Ответ 2

Вы действительно можете использовать Option::as_ref, вам просто нужно использовать его раньше:

impl Bar {
    fn borrow(&self) -> Result<&Box<Foo>, BarErr> {
        self.data.as_ref().ok_or(BarErr::Nope)
    }
}

Есть сопутствующий метод для изменяемых ссылок: Option::as_mut:

impl Bar {
    fn borrow_mut(&mut self) -> Result<&mut Box<Foo>, BarErr> {
        self.data.as_mut().ok_or(BarErr::Nope)
    }
}

Я бы, вероятно, добавил на map чтобы удалить упаковщик Box:

impl Bar {
    fn borrow(&self) -> Result<&Foo, BarErr> {
        self.data.as_ref().ok_or(BarErr::Nope).map(|x| &**x)
    }

    fn borrow_mut(&mut self) -> Result<&mut Foo, BarErr> {
        self.data.as_mut().ok_or(BarErr::Nope).map(|x| &mut **x)
    }
}

Смотрите также:


Начиная с Rust 1.26, эргономика соответствия позволяет писать:

fn borrow(&mut self) -> Result<&Box<Foo>, BarErr> {
    match &self.data {
        Some(e) => Ok(e),
        None => Err(BarErr::Nope),
    }
}