rust - 如何借用对 Option<T> 中内容的引用?

标签 rust

如何从 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`

嗯,好的。也许不会。看起来我想做的事情依稀与 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);
    }
}

我相当确定我尝试做的事情在这里是有效的。

最佳答案

从 Rust 1.26 开始,match ergonomics 允许您编写:

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

在此之前,您可以使用 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)
    }
}

不过,我鼓励删除 Box 包装器。

从 Rust 1.40 开始,您可以使用 Option::as_deref/Option::as_deref_mut :

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

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

在那之前,我可能会使用 map

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

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

使用符合人体工程学的版本,您可以内嵌映射:

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

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

另见:

关于rust - 如何借用对 Option<T> 中内容的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22282117/

相关文章:

rust - `if` 具有自有和借入值的分支,没有 let 绑定(bind)

unit-testing - 在单独的文件中运行 Rust 测试

rust - Python 中的 'pass' 是什么?

http - 如何从 Rust 发送超过 65535 个符号的 GET 请求?

rust - 为什么从 `&' a [u8 ]` to ` &'a mut [u8]` 更改字段会导致生命周期错误?

rust - 我怎样才能让编译器警告我标记为 pub 的未使用代码?

rust - 内存数据库设计

database - rust -我可以使这个dsl​​::find()函数更通用吗?

regex - 如何在恒定时间内不使用额外空间替换字符串中的单个字符?

rust - 用于在 impl 语句上获取所需类型的模式匹配