Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upReturning a borrowed value extends the borrow to the end of the function #40307
Comments
This comment has been minimized.
This comment has been minimized.
|
fn a<'a>(v: &'a mut Vec<String>) -> &'a str {
match v.first::<'a>() {
Some(s) => return &**s as &'a str,
None => {}
}
// Move this here to try to work around non-lexical borrow
v.push("".to_owned());
return "" as &'a str;
}There is no way for it to be otherwise because the signature of I suspect what you are asking for may be unsound. |
This comment has been minimized.
This comment has been minimized.
|
I did find a workaround, but it is specific to fn a(v: &mut Vec<String>) -> &str {
match v.len() {
0 => {
v.push("".to_owned());
""
},
_ => &*v[0]
}
} |
This comment has been minimized.
This comment has been minimized.
|
Yes, that workaround is based on separately checking which case you’re on, then separately taking the borrow. In my non-reduce code the optional borrow is returned by a ~40 lines method that is not easily split into these two parts, unfortunately. |
This comment has been minimized.
This comment has been minimized.
|
I think I’m now convinced that despite my earlier impression, fixing this is in fact non-lexical borrows. Closing in favor of rust-lang/rfcs#811. |
SimonSapin
closed this
Mar 6, 2017
This comment has been minimized.
This comment has been minimized.
|
Was playing around with this and came across unsoundness in a particular case that looks very similar; I think we need to be very careful around non-lexical borrows. // will swap x and y provided the memory addresses are distinct
// in rust they always will be!
fn swap(x: &mut i32, y: &mut i32) {
assert!(*x != 0 && *y != 0, "invalid inputs");
*x ^= *y; *y ^= *x; *x ^= *y;
assert!(*x != 0 && *y != 0, "can't happen");
}
struct Struct<'a> {
reference: Option<&'a mut i32>,
data: i32,
}
impl<'a> Struct<'a> {
fn get(&'a mut self) -> Option<&'a i32> {
match self.reference {
Some(ref s) => Some(s),
None => {
self.reference = Some(&mut self.data);
None
}
}
}
fn do_something(&mut self) {
let x = &mut self.data;
if let Some(ref mut y) = self.reference {
swap(x, y);
}
}
fn violate(&'a mut self) -> &'a i32 {
// needed to overcome the borrow checker
let self_ = unsafe { &mut *(self as *mut Struct<'a>) };
match self.get() {
Some(s) => s,
None => {
// we assume everything is OK to take another borrow
// of self here, since None was returned
self_.do_something();
&self_.data
}
}
}
}
fn main() {
let mut s = Struct {
reference: None,
data: 42,
};
s.violate(); // uh-oh
} |
SimonSapin commentedMar 6, 2017
Consider code reduced to this:
It fails to build in current Rust, as it is the typical case where we want non-lexical borrows. But the usual work-around doesn’t work here
The borrow of
vinv.first()ends at the end of the entire function, not merely at the end of thematchexpression. Returning something other thanscompiles, as the borrow shrinks to the match expression.Is it possible to make
returnnot extend borrows to an entire function without fully implementing non-lexical borrow? Is there another work-around in this case?