Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRefCell::borrow does not pass borrow-check without a seemingly no-op let binding #23338
Comments
SimonSapin
referenced this issue
Mar 13, 2015
Closed
Tracking issue for Rust high-pri or blocking issues for Servo & Gecko #2853
This comment has been minimized.
This comment has been minimized.
|
cc @pnkfelix |
This comment has been minimized.
This comment has been minimized.
|
@SimonSapin has also mentioned that there are some cases (e.g., in our "script" crate) where even this transformation does not work. |
This comment has been minimized.
This comment has been minimized.
|
I got stuck at some point but @jdm managed to find work arounds servo/servo@0c12dd9 |
brson
added
the
I-nominated
label
Mar 16, 2015
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
cc #22321 |
This comment has been minimized.
This comment has been minimized.
|
Standalone test case: use std::cell::RefCell;
fn foo(x: RefCell<String>) -> String {
x.borrow().clone()
}
fn main() { } |
This comment has been minimized.
This comment has been minimized.
|
leaving nominated tag to talk again next week, assigning to self to investigate. |
pnkfelix
self-assigned this
Mar 19, 2015
This comment has been minimized.
This comment has been minimized.
|
Another work-around worth noting (if only because it provides a hint at the problem here): fn foo(x: RefCell<String>) -> String {
let t = x.borrow().clone();
t // this works!
}I think this is an artifact of our temporary r-value rules, in particular the one that says that all temporaries for the tail expression in a block are assigned the lifetime of the parent of the block (that is, they can outlive the block), and thus the |
pnkfelix
removed
the
I-nominated
label
Apr 2, 2015
This comment has been minimized.
This comment has been minimized.
|
(Having said that, I would like to see if we can still fix this in some way. But I no longer see this as a potential smoking gun pointing at some fundamental flaw in the system. So I've removed the I-nominated tag.) More details for those interested: the thing that comes up is this:
We could hack in special support for this case (i.e. some hack in terms of how
use std::cell::RefCell;
fn foo(x: RefCell<String>) -> String {
let result = {
let t = x;
t.borrow().clone()
};
result
}
fn main() { }(but maybe that is acceptable...) |
This comment has been minimized.
This comment has been minimized.
|
Actually, according to the current dynamic semantics, fn parameters already are torn down after the fn body is torn down. So it should be entirely sound (perhaps even a legitimate bug fix, not sure) to make the code-extents reflect the fact that the temporaries from the tail expression for the fn-body are destroyed before the parameters are dropped (i.e., that the parameters strictly outlive all of the r-value temporaries of the fn body). Here is some demo code illustrating this (note also that the behavior differs if one passes struct D(&'static str, u32);
impl Drop for D {
fn drop(&mut self) {
println!("Dropping D({}, {})", self.0, self.1);
}
}
impl D {
fn incr(&self, name: &'static str) -> D {
D(name, self.1 + 1)
}
}
#[cfg(not(nested))]
fn foo(a1: D, b1: D) -> (&'static str, D) {
let _b2 = b1.incr("b");
let _a2 = a1.incr("a");
let b3 = b1.incr("temp1").incr("b");
println!("made b2 a2 and b3");
("foo_direct", b3.incr("temp2").incr("b"))
}
#[cfg(nested)]
fn foo(a1: D, b1: D) -> (&'static str, D) {
let _b2 = b1.incr("b");
{
let _a2 = a1.incr("a");
let b3 = b1.incr("temp1").incr("b");
println!("made b2 a2 and b3");
("foo_nested", b3.incr("temp2").incr("b"))
}
}
fn main() {
let (name, result) = foo(D("param_a", 1), D("param_b", 1));
println!("called {}, got result D({}, {})",
name, result.0, result.1);
}(The reason that its interesting that the behavior differs with and without fn function(args ...) -> result { stmts ...; expr } to fn function(args ...) -> result { { stmts ...; expr } }is not semantics preserving; it changes the time at which the temporaries within |
steveklabnik
added
the
A-typesystem
label
Apr 3, 2015
pnkfelix
added
A-destructors
A-lifetimes
and removed
A-lifetimes
A-typesystem
labels
Apr 3, 2015
pnkfelix
referenced this issue
Apr 3, 2015
Merged
Encode more precise scoping rules for function params #24021
This comment has been minimized.
This comment has been minimized.
|
Okay, I'm pretty happy with the solution given in #24021. |
bors
added a commit
that referenced
this issue
Apr 8, 2015
bors
closed this
in
#24021
Apr 8, 2015
stshine
referenced this issue
Sep 28, 2016
Merged
Make document url mutable and implement location.replace() #13418
This comment has been minimized.
This comment has been minimized.
|
@pnkfelix this still is present w.r.t expression blocks, though, which are pretty useful when attempting to control the length of borrows. Using workaround in #23338 (comment) for now but it would be great for this to work nicely at some point, it led me to think I was going crazy |
SimonSapin commentedMar 13, 2015
When upgrading Rust in Servo, many usages of
RefCell::borrowthat were previously fine now cause borrow-check errors like this:This error message makes no sense to me, the two blocks it talks about are the same.
This can be worked around by binding the result of
.borrow()withletbefore using it, enough though such a change looks like a no-op:fn StatusText(self) -> ByteString { - self.status_text.borrow().clone() + let status_text = self.status_text.borrow(); + status_text.clone() }