Example Code:
struct Foo;
impl Foo {
fn foo(&mut self) -> Result<&i32, String> {
match self.bar() {
Some(d) => Ok(d),
None => {
if self.check() {
Err(String::from("Error type 1"))
} else {
Err(String::from("Error type 2"))
}
}
}
}
fn bar(&mut self) -> Option<&i32> {
todo!()
}
fn check(&self) -> bool {
todo!()
}
}
Rust playground Code
Problem
In the Foo::foo function:
self.bar() requires mutable borrow.
self.check() requires immutable borrow.
The above code fails to compile. The lifetime checker complains about the two borrow (mutable and immutable).
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
--> src/lib.rs:8:20
|
4 | fn foo(&mut self) -> Result<&i32, String> {
| - let's call the lifetime of this reference `'1`
5 | match self.bar() {
| ---------- mutable borrow occurs here
6 | Some(d) => Ok(d),
| ----- returning this value requires that `*self` is borrowed for `'1`
7 | None => {
8 | if self.check() {
| ^^^^^^^^^^^^ immutable borrow occurs here
Questions
Question 1
Why the compiler prevent me to compile this code?
Clearly I am missing something here, but...
The immutable borrow (i.e. self.check()) only happens in the None branch. The previous mutable borrow (i.e. self.bar()) should not bring any impact there, should it?
The same code might be written:
if let Some(d) = self.bar() {
return Ok(d);
}
// What lifetime of `self.bar` is needed at this point?
if self.check() {
// ...
} else {
// ...
}
Question 2
How can I solve this problem?
Please note:
I do not want to move the check (i.e. self.check()) before the match. Merely because of performances: self.check might be expensive and in the "cold" path. If self.bar() returns Some I want to return as soon as possible from the function.
I do not want to to introduce runtime overhead. (E.g., dynamic check for borrow -> RefCell).
Of course, this is a dummy example. Just for the sake of demonstration. So be aware about it. For example:
&'a i32 represent an example returned "data" that requires lifetime. In my real code you can imagine I have a complex object which holds some references (and thus require lifetime). E.g., struct ComplexObject<'a>.
Answer 1
The problem is that in Some(d) => Ok(d) you're still holding a reference &i32 with the same lifetime as &mut self of the self.bar() call. Lifetime elisions hide this, but the code would desugar into something like this:
fn bar<'a>(&'a mut self) -> Option<&'a i32> {
todo!()
}
The self.bar() result has lifetime 'a, so does d in Some(d). Since you're returning d from the function, self.bar() needs to stay mutably borrowed until the end of the function. This makes self.check() impossible to be borrowed.
To better visualize the issue:
use std::rc::Rc;
struct Foo;
impl Foo {
fn foo<'b>(&'b mut self) -> Result<&'b i32, String> {
match self.bar() { // --+- "&'a mut self" borrowed
Some(d) => Ok(d), // --+- "'a" becomes "'b"
None => { // |
if self.check() { // --+- "&'c self" borrow failed attempt
Err(String::from("Error type 1"))
} else { // |
Err(String::from("Error type 2"))
} // |
} // |
} // |
} // --+- "&'a mut self" valid until here
fn bar<'a>(&'a mut self) -> Option<&'a i32> {
todo!()
}
fn check<'c>(&'c self) -> bool {
todo!()
}
}
The return value of self.foo is &'b i32, which needs the same lifetime as the return value of bar, so 'a needs to live at least as long as 'b, thus &'a mut self borrowed in self.bar stays borrowed until the end of self.foo.
Answer 2
There's three options I'd consider:
Change the return value of bar to be Option<i32> if moving is an option:
struct Foo;
impl Foo {
fn foo(&mut self) -> Result<i32, String> {
match self.bar() {
Some(d) => Ok(d),
None => {
if self.check() {
Err(String::from("Error type 1"))
} else {
Err(String::from("Error type 2"))
}
}
}
}
fn bar(&mut self) -> Option<i32> {
todo!()
}
fn check(&self) -> bool {
todo!()
}
}
Split self.bar() into two separate functions, one that does the mutable operation and moves the ownership from the function to the callee, and one that returns Option<&i32> but borrows immutably:
struct Foo;
impl Foo {
fn foo(&mut self) -> Result<&i32, String> {
let bar = self.bar_mut();
match self.bar(bar) {
Some(d) => Ok(d),
None => {
if self.check() {
Err(String::from("Error type 1"))
} else {
Err(String::from("Error type 2"))
}
}
}
}
fn bar_mut(&mut self) -> Option<i32> {
todo!()
}
fn bar(&self, bar: Option<i32>) -> Option<&i32> {
todo!()
}
fn check(&self) -> bool {
todo!()
}
}
Use Rc for multiple ownership if you cannot move the object at all:
use std::rc::Rc;
struct Foo;
impl Foo {
fn foo(&mut self) -> Result<Rc<i32>, String> {
match self.bar() {
Some(d) => Ok(d),
None => {
if self.check() {
Err(String::from("Error type 1"))
} else {
Err(String::from("Error type 2"))
}
}
}
}
fn bar(&mut self) -> Option<Rc<i32>> {
// Rc::clone the value here
todo!()
}
fn check(&self) -> bool {
todo!()
}
}
Related
I have a struct that lazily load data into an inner vector (but for the example this is ommited). Then I am implementing IntoIterator and Iterator for the IntoIterator type:
struct EntryOwned(u32);
struct Entry<'a>(&'a u32);
impl<'a> EntryOwned {
fn to_entry(&'a self) -> Entry<'a> {
Entry(&self.0)
}
}
struct LazyStruct {
cache: Vec<EntryOwned>,
}
impl<'a> LazyStruct {
fn new(data: Vec<EntryOwned>) -> LazyStruct {
Self {
cache: data,
}
}
fn get_entry(&'a self, index: usize) -> Option<Entry<'a>> {
match self.cache.get(index) {
Some(entry_owned) => Some(entry_owned.to_entry()),
None => None,
}
}
}
impl<'a> IntoIterator for &'a mut LazyStruct {
type Item = Entry<'a>;
type IntoIter = LazyStructIter<'a>;
fn into_iter(self) -> Self::IntoIter {
LazyStructIter {
inner: self,
current_index: 0,
}
}
}
struct LazyStructIter<'a> {
inner: &'a mut LazyStruct,
current_index: usize,
}
impl<'a> LazyStructIter<'a> {
fn next_item(&'a mut self) -> Option<Entry<'a>> {
if self.current_index > self.inner.cache.len() {
return None;
}
let ret = self.inner.get_entry(self.current_index);
self.current_index += 1;
ret
}
}
impl<'a> Iterator for LazyStructIter<'a> {
type Item = Entry<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.next_item()
}
}
Which yields me to a lifetime issue:
Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:64:14
|
64 | self.next_item()
| ^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 63:5...
--> src/main.rs:63:5
|
63 | / fn next(&mut self) -> Option<Self::Item> {
64 | | self.next_item()
65 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:64:9
|
64 | self.next_item()
| ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 60:6...
--> src/main.rs:60:6
|
60 | impl<'a> Iterator for LazyStructIter<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:64:14
|
64 | self.next_item()
| ^^^^^^^^^
= note: expected `&mut LazyStructIter<'_>`
found `&mut LazyStructIter<'a>`
The lifetime of the references should be bind to the LazyStruct, but I cannot change the Iter trait do not accept any lifetime specifier.
I have already check some answers to similar issues:
Iterator returning items by reference, lifetime issue
How do I write an iterator that returns references to itself?
EDIT: The Entry is a more complex data structure and it is not possible to copy it. I have to bind it to a specific lifetime since it contains references to things.
One of them points that the iterator should hold a reference to the original collection instead of owning it since it is not allowed to return a reference with the self lifetime. But I could't make it work.
So, how should I play the references here?
Here is the playground example
You can't.
You require that each item returned by next() has a reference with a lifetime tied to the
existence of the LazyStructIter iterator.
This it is not possible with the Iterator interface:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
But it would be possible with a trait defined as:
trait FakeIterator<'a> {
type Item;
fn next(&'a mut self) -> std::option::Option<Self::Item>;
}
impl<'a> FakeIterator<'a> for LazyStructIter<'a> {
type Item = Entry<'a>;
fn next(&'a mut self) -> Option<Self::Item> {
self.next_item()
}
}
Tha capability to have a kind of iterator which returns items borrowing from self is the goal of
RFC 1598 - GAT.
See also this article for a collection of workarounds about this topic.
Generally you don't need to specify lifetimes in rust unless 1 of two things is true, the reference is bound to an owner that isn't known, or a reference that is static. Rust says it expected '_ lifetime, which means it understands the ownership and borrows on its own.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c54f5f2f24e635dcfaa0fcbf69fa4ce0
struct EntryOwned(u32);
struct Entry(u32);
impl EntryOwned {
fn to_entry(&self) -> Entry {
Entry(self.0)
}
}
struct LazyStruct {
cache: Vec<EntryOwned>,
}
impl LazyStruct {
fn new(data: Vec<EntryOwned>) -> LazyStruct {
Self {
cache: data,
}
}
fn get_entry(&self, index: usize) -> Option<Entry> {
match self.cache.get(index) {
Some(entry_owned) => Some(entry_owned.to_entry()),
None => None,
}
}
}
impl IntoIterator for LazyStruct {
type Item = Entry;
type IntoIter = LazyStructIter;
fn into_iter(self) -> Self::IntoIter {
LazyStructIter {
inner: self,
current_index: 0,
}
}
}
struct LazyStructIter {
inner: LazyStruct,
current_index: usize,
}
impl LazyStructIter {
fn next_item(&mut self) -> Option<Entry> {
if self.current_index > self.inner.cache.len() {
return None;
}
let ret = self.inner.get_entry(self.current_index);
self.current_index += 1;
ret
}
}
impl Iterator for LazyStructIter {
type Item = Entry;
fn next(&mut self) -> Option<Self::Item> {
self.next_item()
}
}
fn main() {
// let json_data = r#"{"item_1" : 10, "item_2" : 100, "item_3" : 1000}"#;
// if let Ok(data) = serde_json::from_str::<A>(json_data) {
// println!("{:?}", data);
// }
// else if let Ok(data) = serde_json::from_str::<B>(json_data) {
// println!("{:?}", data);
// }
for _ in 0..0 {
println!("foo");
}
}
After removing the lifetime specifications, it compiles fine and runs.
I created a library to deal with digraphs: nodes that link (reference counted) to zero or one other nodes (as in linked lists, but in a digraph a node can be linked to by more than one node).
I am trying to use my library to create a list with a current node:
struct ListWithPointer<'a> {
pub nodes: DigraphNodeRef<String>,
pub current_node: Option<&'a mut DigraphNodeRef<String>>,
}
current_node points to a link in the list.
Now I am trying to move current node to the next element of the list (or to the beginning if the list ended):
fn next_node<'a>(this: &'a mut ListWithPointer<'a>) {
if this.current_node.is_some() {
this.current_node.iter_mut().for_each(|a| {
(*a).as_rc_mut().iter_mut()
.for_each(|rc| this.current_node = Some(&mut Arc::get_mut(rc).unwrap().next));
});
} else {
this.current_node = Some(&mut this.nodes);
}
}
but whatever I do, it fails with an error like:
error[E0500]: closure requires unique access to `this.current_node` but it is already borrowed
--> src/lib.rs:150:51
|
148 | fn next_node<'a>(this: &'a mut ListWithPointer<'a>) {
| -- lifetime `'a` defined here
149 | if this.current_node.is_some() {
150 | this.current_node.iter_mut().for_each(|a| {
| ---------------------------- ^^^ closure construction occurs here
| |
| borrow occurs here
| argument requires that `this.current_node` is borrowed for `'a`
151 | (*a).as_rc_mut().iter_mut()
152 | .for_each(|rc| this.current_node = Some(&mut Arc::get_mut(rc).unwrap().next));
| ----------------- second borrow occurs due to use of `this.current_node` in closure
Help to rewrite without errors.
Here is the library code:
use std::sync::Arc;
#[derive(Clone)]
pub struct DigraphNode<T> {
pub next: DigraphNodeRef<T>, // I made it `pub` to be able `item.next.next()` to remove an item from the middle.
data: T,
}
impl<T> DigraphNode<T> {
fn new(next: DigraphNodeRef<T>, data: T) -> Self {
Self { next, data }
}
}
pub struct DigraphNodeRef<T> {
rc: Option<Arc<DigraphNode<T>>>,
}
impl<T> DigraphNodeRef<T> {
pub fn new() -> Self {
Self {
rc: None
}
}
pub fn from_node(value: DigraphNode<T>) -> Self {
Self::from(Some(Arc::new(value)))
}
pub fn from(rc: Option<Arc<DigraphNode<T>>>) -> Self {
Self {
rc
}
}
pub fn as_rc(&self) -> &Option<Arc<DigraphNode<T>>> {
&self.rc
}
pub fn as_rc_mut(&mut self) -> &mut Option<Arc<DigraphNode<T>>> {
&mut self.rc
}
pub fn is_none(&self) -> bool {
self.rc.is_none()
}
pub fn remove(&mut self) -> bool {
if let Some(rc) = self.rc.clone() {
self.rc = rc.next.rc.clone();
true
} else {
false
}
}
pub fn prepend(&mut self, value: T) -> Self {
let new_node = DigraphNode::new(self.clone(), value);
let new_node_ref = DigraphNodeRef::from_node(new_node);
*self = new_node_ref.clone();
new_node_ref
}
pub fn node(&self) -> Option<DigraphNode<T>>
where T: Clone
{
self.rc.clone().map(|node| (*node).clone())
}
/// TODO: Should return a reference.
pub fn data(&self) -> Option<T>
where T: Clone
{
self.rc.clone().map(|node| (*node).data.clone())
}
pub fn values(self) -> DigraphNodeValuesIterator<T> {
DigraphNodeValuesIterator {
underlying: self.clone()
}
}
}
impl<T> Clone for DigraphNodeRef<T> {
fn clone(&self) -> Self {
Self { rc: self.rc.clone() }
}
}
impl<T> Iterator for DigraphNodeRef<T> {
type Item = Arc<DigraphNode<T>>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(rc) = self.rc.clone() {
self.rc = rc.next.rc.clone();
Some(rc.clone())
} else {
None
}
}
}
pub struct DigraphNodeValuesIterator<T> {
underlying: DigraphNodeRef<T>,
}
impl<T: Clone> Iterator for DigraphNodeValuesIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.underlying.next().map(|node| node.data.clone())
}
}
In Rust the mutable access is ensured to be exclusive, i.e. if you hold a reference, some other code can't grab a mutable reference.
Problem is this line:
this.current_node.iter_mut().for_each(...)
It grabs a mutable access to current_node, so it can't regain it again down the line.
Not to mention that iterating over Option is a strange decision.
If you want to move current_node to a different place, I'd try to reorganize your code such that reads are separate from writes, and they are performed in a sequence, instead of trying to do it in one go:
// detach the current_node for moving
if let Some(current_node_to_move) = this.current_node.take() {
let new_current_node_ref: &mut ... = ... // find new location logic
new_current_node_ref.replace(current_node_to_move);
} else {
...
}
Here in line 1 it does a write None update to current_node via this, but immediately relinquishes the mutable reference. Line 2 does a read (search), but also grabs a mutable reference to a new location. Line 3 writes to this location.
To get the linked list implementation right, I recommend https://rust-unofficial.github.io/too-many-lists/
I have a struct that lazily load data into an inner vector (but for the example this is ommited). Then I am implementing IntoIterator and Iterator for the IntoIterator type:
struct EntryOwned(u32);
struct Entry<'a>(&'a u32);
impl<'a> EntryOwned {
fn to_entry(&'a self) -> Entry<'a> {
Entry(&self.0)
}
}
struct LazyStruct {
cache: Vec<EntryOwned>,
}
impl<'a> LazyStruct {
fn new(data: Vec<EntryOwned>) -> LazyStruct {
Self {
cache: data,
}
}
fn get_entry(&'a self, index: usize) -> Option<Entry<'a>> {
match self.cache.get(index) {
Some(entry_owned) => Some(entry_owned.to_entry()),
None => None,
}
}
}
impl<'a> IntoIterator for &'a mut LazyStruct {
type Item = Entry<'a>;
type IntoIter = LazyStructIter<'a>;
fn into_iter(self) -> Self::IntoIter {
LazyStructIter {
inner: self,
current_index: 0,
}
}
}
struct LazyStructIter<'a> {
inner: &'a mut LazyStruct,
current_index: usize,
}
impl<'a> LazyStructIter<'a> {
fn next_item(&'a mut self) -> Option<Entry<'a>> {
if self.current_index > self.inner.cache.len() {
return None;
}
let ret = self.inner.get_entry(self.current_index);
self.current_index += 1;
ret
}
}
impl<'a> Iterator for LazyStructIter<'a> {
type Item = Entry<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.next_item()
}
}
Which yields me to a lifetime issue:
Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:64:14
|
64 | self.next_item()
| ^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 63:5...
--> src/main.rs:63:5
|
63 | / fn next(&mut self) -> Option<Self::Item> {
64 | | self.next_item()
65 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:64:9
|
64 | self.next_item()
| ^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 60:6...
--> src/main.rs:60:6
|
60 | impl<'a> Iterator for LazyStructIter<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:64:14
|
64 | self.next_item()
| ^^^^^^^^^
= note: expected `&mut LazyStructIter<'_>`
found `&mut LazyStructIter<'a>`
The lifetime of the references should be bind to the LazyStruct, but I cannot change the Iter trait do not accept any lifetime specifier.
I have already check some answers to similar issues:
Iterator returning items by reference, lifetime issue
How do I write an iterator that returns references to itself?
EDIT: The Entry is a more complex data structure and it is not possible to copy it. I have to bind it to a specific lifetime since it contains references to things.
One of them points that the iterator should hold a reference to the original collection instead of owning it since it is not allowed to return a reference with the self lifetime. But I could't make it work.
So, how should I play the references here?
Here is the playground example
You can't.
You require that each item returned by next() has a reference with a lifetime tied to the
existence of the LazyStructIter iterator.
This it is not possible with the Iterator interface:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
But it would be possible with a trait defined as:
trait FakeIterator<'a> {
type Item;
fn next(&'a mut self) -> std::option::Option<Self::Item>;
}
impl<'a> FakeIterator<'a> for LazyStructIter<'a> {
type Item = Entry<'a>;
fn next(&'a mut self) -> Option<Self::Item> {
self.next_item()
}
}
Tha capability to have a kind of iterator which returns items borrowing from self is the goal of
RFC 1598 - GAT.
See also this article for a collection of workarounds about this topic.
Generally you don't need to specify lifetimes in rust unless 1 of two things is true, the reference is bound to an owner that isn't known, or a reference that is static. Rust says it expected '_ lifetime, which means it understands the ownership and borrows on its own.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c54f5f2f24e635dcfaa0fcbf69fa4ce0
struct EntryOwned(u32);
struct Entry(u32);
impl EntryOwned {
fn to_entry(&self) -> Entry {
Entry(self.0)
}
}
struct LazyStruct {
cache: Vec<EntryOwned>,
}
impl LazyStruct {
fn new(data: Vec<EntryOwned>) -> LazyStruct {
Self {
cache: data,
}
}
fn get_entry(&self, index: usize) -> Option<Entry> {
match self.cache.get(index) {
Some(entry_owned) => Some(entry_owned.to_entry()),
None => None,
}
}
}
impl IntoIterator for LazyStruct {
type Item = Entry;
type IntoIter = LazyStructIter;
fn into_iter(self) -> Self::IntoIter {
LazyStructIter {
inner: self,
current_index: 0,
}
}
}
struct LazyStructIter {
inner: LazyStruct,
current_index: usize,
}
impl LazyStructIter {
fn next_item(&mut self) -> Option<Entry> {
if self.current_index > self.inner.cache.len() {
return None;
}
let ret = self.inner.get_entry(self.current_index);
self.current_index += 1;
ret
}
}
impl Iterator for LazyStructIter {
type Item = Entry;
fn next(&mut self) -> Option<Self::Item> {
self.next_item()
}
}
fn main() {
// let json_data = r#"{"item_1" : 10, "item_2" : 100, "item_3" : 1000}"#;
// if let Ok(data) = serde_json::from_str::<A>(json_data) {
// println!("{:?}", data);
// }
// else if let Ok(data) = serde_json::from_str::<B>(json_data) {
// println!("{:?}", data);
// }
for _ in 0..0 {
println!("foo");
}
}
After removing the lifetime specifications, it compiles fine and runs.
I'm trying to implement lazy "thunks" in Rust and I just can't figure out how to get my code to pass the borrow checker. The basic idea is that a Thunk<T> can only be in one of two ThunkStates:
Forced which carries its value of type T;
Unforced, which carries a boxed closure that returns a T.
My naïve code goes like this:
pub struct Thunk<T>(ThunkState<T>);
enum ThunkState<T> {
Forced(T),
Unforced(Box<Fn() -> T>),
}
impl<T> Thunk<T> {
pub fn new<F>(f: F) -> Thunk<T>
where
F: Fn() -> T + 'static,
{
Thunk(ThunkState::Unforced(Box::new(f)))
}
pub fn get(&mut self) -> &T {
match self.0 {
ThunkState::Forced(ref t) => &t,
ThunkState::Unforced(ref f) => {
// TROUBLE HERE
self.0 = ThunkState::Forced(f());
self.get()
}
}
}
}
I get the following two compilation errors:
error[E0506]: cannot assign to `self.0` because it is borrowed
--> src/main.rs:21:17
|
19 | ThunkState::Unforced(ref f) => {
| ----- borrow of `self.0` occurs here
20 | // TROUBLE HERE
21 | self.0 = ThunkState::Forced(f());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.0` occurs here
error[E0502]: cannot borrow `*self` as mutable because `self.0.0` is also borrowed as immutable
--> src/main.rs:22:17
|
19 | ThunkState::Unforced(ref f) => {
| ----- immutable borrow occurs here
...
22 | self.get()
| ^^^^ mutable borrow occurs here
23 | }
24 | }
| - immutable borrow ends here
I've gone through various iterations of trying out stuff (e.g., match *self.0, using &mut in the ThunkState patterns, and a few variations), but try as I may, I can't figure out how to fix it.
Am I attempting to do something that doesn't make sense?
If not, what makes this example so tricky, and how do I get it right?
Staring at it a bit more, I've formulated the following hypothesis: the assignment to self.0 would invalidate the f reference in that match branch. Is this right? And if so, then how do I achieve what I'm trying to do—discard the closure after I use it?
Your original code works as-is with non-lexical lifetimes enabled (available in the 2018 edition):
pub struct Thunk<T>(ThunkState<T>);
enum ThunkState<T> {
Forced(T),
Unforced(Box<Fn() -> T>),
}
impl<T> Thunk<T> {
pub fn new<F>(f: F) -> Thunk<T>
where
F: Fn() -> T + 'static,
{
Thunk(ThunkState::Unforced(Box::new(f)))
}
pub fn get(&mut self) -> &T {
match self.0 {
ThunkState::Forced(ref t) => t,
ThunkState::Unforced(ref f) => {
self.0 = ThunkState::Forced(f());
self.get()
}
}
}
}
This is now supported because the tracking of what is borrowed in which match arm is now more precise.
This is indeed a tricky problem, but it is possible. For stuff like this, it's often a good idea to search for helpful functions in the mem module.
I've come up with a solution, but I think that there is still a lot of room for improvement.
pub fn get(&mut self) -> &T {
let mut f = None;
if let ThunkState::Unforced(ref mut f_ref) = self.0 {
f = Some(std::mem::replace(f_ref, unsafe {
std::mem::uninitialized()
}));
}
if let Some(f) = f {
self.0 = ThunkState::Forced(f());
}
match self.0 {
ThunkState::Forced(ref t) => &t,
_ => unreachable!(),
}
}
This compiles fine, at least. The trick is to use mem::replace to get the important value out of self first. Additionally, you can avoid the unsafe by creating some kind of dummy value (like Box::new(|| panic!())).
I found something that semi-works:
pub struct Thunk<T>(ThunkState<T>);
enum ThunkState<T> {
Forced(T),
Unforced(Box<Fn() -> T>),
}
impl<T> Thunk<T> {
pub fn new<F>(f: F) -> Thunk<T>
where
F: Fn() -> T + 'static,
{
Thunk(ThunkState::Unforced(Box::new(f)))
}
pub fn get(&mut self) -> &T {
match self.0 {
ThunkState::Forced(ref t) => &t,
// Don't actually bind a variable to the boxed closure here.
ThunkState::Unforced(_) => {
self.0 = ThunkState::Forced(self.0.compute());
self.get()
}
}
}
}
impl<T> ThunkState<T> {
fn compute(&self) -> T {
match self {
&ThunkState::Unforced(ref f) => f(),
// FIXME: get rid of this panic?
&ThunkState::Forced(_) => panic!("case never used"),
}
}
}
It compiles, but after trying to use this type what I've learned is that I probably need interior mutability.
The problem is that you're trying to do too much (for the sake of avoiding return) within the lexical borrowing context of match self.0 { ... }.
What you can do is:
Move results of calculations performed on values you need to borrow from self.0 into variables defined in an outer scope.
Exit early on paths where those values aren't needed
Consume the values after the match statement
Applied to your example, a solution could be:
pub struct Thunk<T>(ThunkState<T>);
enum ThunkState<T> {
Forced(T),
Unforced(Box<Fn() -> T>),
}
impl<T> Thunk<T> {
pub fn new<F>(f: F) -> Thunk<T>
where
F: Fn() -> T + 'static,
{
Thunk(ThunkState::Unforced(Box::new(f)))
}
pub fn get(&mut self) -> &T {
let func_result: T;
match self.0 {
ThunkState::Forced(ref t) => {
return &t;
}
ThunkState::Unforced(ref f) => {
func_result = f();
}
}
self.0 = ThunkState::Forced(func_result);
self.get()
}
}
I have a problem with self borrowing in a match expression:
fn add_once(&'t mut self, p_name: &'t str) -> Box<Element> {
match self.get(p_name) {
Some(x) => Box::new(*x),
None => self.add(p_name),
}
}
The signature of the get() and add() functions are:
fn get(&self, p_name: &str) -> Option<&Element>
fn add(&'t mut self, p_name: &'t str) -> Box<Element>
The compiler refuses this code:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:38:21
|
36 | match self.get(p_name) {
| ---- immutable borrow occurs here
37 | Some(x) => Box::new(*x),
38 | None => self.add(p_name),
| ^^^^ mutable borrow occurs here
39 | }
40 | }
| - immutable borrow ends here
I get that, but I don't see how I can rewrite the match expression.
In a related question, it was solved by having the match return a value and then calling the function. However, that doesn't work here because the meaning of the conditional is not to choose a value but selectively execute an action.
The full code sample is below:
struct Element<'e> {
name: &'e str,
}
impl<'e> Element<'e> {
fn new(p_name: &str) -> Element {
Element { name: p_name }
}
}
struct Top<'t> {
list: Vec<Element<'t>>,
}
impl<'t> Top<'t> {
fn new() -> Top<'t> {
Top { list: vec![] }
}
fn get(&self, p_name: &str) -> Option<&Element> {
for element in self.list.iter() {
if element.name == p_name {
return Some(element);
}
}
None
}
fn add(&'t mut self, p_name: &'t str) -> Box<Element> {
let new_element = Box::new(Element::new(p_name));
self.list.push(*new_element);
return new_element;
}
fn add_once(&'t mut self, p_name: &'t str) -> Box<Element> {
match self.get(p_name) {
Some(x) => Box::new(*x),
None => self.add(p_name),
}
}
}
fn main() {
let mut t = Top::new();
let plop1 = t.add_once("plop1");
let plop2 = t.add_once("plop1");
}
Let's fix the design issues first. The main issue is lifetime conflation:
struct Top<'t> {
list: Vec<Element<'t>>,
}
impl<'t> Top<'t> {
fn add(&'t mut self, p_name: &'t str) -> Box<Element>;
fn add_once(&'t mut self, p_name: &'t str) -> Box<Element>;
}
You assert that self should live at least as long as 't, which is itself the lifetime bound of the references it will contain. It is not actually what you need, self should live less than 't to guarantee that any &'t is still alive and kicking until self dies.
If we change this, we can painlessly return references to Element:
impl<'t> Top<'t> {
fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t>;
}
Note that the lifetime 'a of the reference to Element is different (and actually will be shorter) than the lifetime 't of its contained reference.
With that out of the way, this should fix the functions:
fn position(&self, p_name: &str) -> Option<usize> {
self.list.iter().position(|e| e.name == p_name)
}
fn add<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
self.list.push(Element::new(p_name));
&self.list[self.list.len() - 1]
}
fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
if let Some(p) = self.position(p_name) {
return &self.list[p];
}
self.add(p_name)
}
And position can be reused for get:
fn get<'a>(&'a self, p_name: &str) -> Option<&'a Element<'t>> {
self.position(p_name).map(|pos| &self.list[pos])
}
I would expect the following to work in a perfect world:
fn add_once<'a>(&'a mut self, p_name: &'t str) -> &'a Element<'t> {
match self.get(p_name) {
Some(x) => x,
None => self.add(p_name)
}
}
However, I recall a discussion in which it was determined that the borrow-checker was not lax enough: the scope of the borrow induced by self.get is computed to be the entire match expression even though in the None branch the temporary cannot be accessed.
This should be fixed once "Non-Lexical Lifetimes" are incorporated in Rust, which is a work in progress.