Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I borrow a reference to what is inside an Option<T>?

Tags:

rust

How do I pull a reference out of an Option and pass it back with the specific lifespan of the caller?

Specifically, I want to borrow a reference to a Box<Foo> from a Bar that has an Option<Box<Foo>> in it. I thought I would be able to do:

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

...but that results in:

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. Maybe not. It looks vaguely like what I want to do is related to Option::as_ref, like maybe I could do:

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

...but, that doesn't work either.

Full code I'm having trouble with:

#[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);     } } 

I'm reasonably sure what I've trying to do is valid here.

like image 248
Doug Avatar asked Mar 09 '14 12:03

Doug


People also ask

How do you borrow variables in Rust?

An Example of Borrowing in Rust You can borrow the ownership of a variable by referencing the owner using the ampersand (&) symbol. Without borrowing by referencing, the program would panic. It would violate the ownership rule that a value can have one owner, and two variables cannot point to the same memory location.

What is borrowing in Rust?

What is Borrowing? When a function transfers its control over a variable/value to another function temporarily, for a while, it is called borrowing. This is achieved by passing a reference to the variable (& var_name) rather than passing the variable/value itself to the function.

What is a reference in Rust?

A reference represents a borrow of some owned value. You can get one by using the & or &mut operators on a value, or by using a ref or ref mut pattern.


1 Answers

As of Rust 1.26, match ergonomics allows you to write:

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

Prior to that, you can use Option::as_ref, you just need to use it earlier:

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

There's a companion method for mutable references: Option::as_mut:

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

I'd encourage removing the Box wrapper though.

Since Rust 1.40, you can use 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)     } } 

Before then, I'd probably use a 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)     } } 

With the match ergonomics version, you can do the mapping inline:

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),         }     } } 

See also:

  • Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
like image 90
Shepmaster Avatar answered Sep 29 '22 23:09

Shepmaster