-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking Issue for std::cell::{Ref, RefMut}::leak
#69099
Comments
…tolnay Add undo_leak to reset RefCell borrow state This method is complementary for the feature cell_leak added in an earlier PR. It allows *safely* reverting the effects of leaking a borrow guard by statically proving that such a guard could not longer exist. This was not added to the existing `get_mut` out of concern of impacting the complexity of the otherwise pure pointer cast and because the name `get_mut` poorly communicates the intent of resetting remaining borrows. This is a follow-up to rust-lang#68712 and uses the same tracking issue, rust-lang#69099, as these methods deal with the same mechanism and the idea came up [in a review comment](rust-lang#68712 (comment)). @dtolnay who reviewed the prior PR. cc @RalfJung
Came accross the lifetime issue again when `Book::running_balance` returns `Transaction<'a>` where `'a` is of the book. This too does not cross the `std::cell::Ref<'_, T>` boundry. However, there is a way to get to the underlying lifetime through an unstable feature 'cell_leak' see rust-lang/rust#69099 This allows the borrow checker to life-check!
We discussed this during the libs meeting today and feel that there is a lack of a specific use case for these methods. @HeroicKatora (or anyone else) could you describe how you are using these methods in your own code? In particular we feel that |
To expand on my original use case, consider an embedded network stack. More Since multiple independent actors are supposed to access the table by methods Note that each /// A slice of ordered values, with bisect lookup.
/// Note: builtin unsizing by choosing `U = [T; N]`.
pub struct OrdSlice<T, U: ?Sized = [T]>(PhantomData<T>, U);
mod netstack {
pub struct Runtime<'net> {
_foo: &'net mut OrdSlice<Addr>,
}
}
mod embedded_app {
pub struct FindFoo<'net> {
target: &'net RefCell<OrdSlice<Addr>>,
}
enum Network<'net> {
Setup {
target: &'net RefCell<OrdSlice<Addr>>,
with_foo_service: Option<FindFoo<'net>>,
… },
Running {
net: Runtime<'net>,
… },
}
} There are several key reasons why Actual usage (with initialization for completeness) is then as follows: fn ingress_step<'net>(
network: &mut Network<'net>,
queues: &mut …,
…
) {
match network {
Setup { target, with_foo_service, … } => {
// some setup io..
if let Some(foo) = &with_foo_service {
foo.ingress(queues);
}
// Done with setup?
if !with_foo_service.as_ref().map_or(true, FindFoo::is_done) {
return;
}
// Switch to active service mode.
network = Network::Running {
net: RefMut::leak(target.borrow_mut()), …
};
},
Running { net } => route_packets(net, queues),
}
} Which we can run with error recovery by utilizing // Modified and shortened obviously..
fn run(addr: &RefCell<OrdSlice<Addr>>) -> Result<()> {
let mut state = Network::new(addr);
let mut queues = …;
loop {
ingress(&mut state, &mut queues);
egress(&mut state, &mut queues);
queues.errors()?;
}
Ok(())
}
fn main() {
let addr: OrdSlice<_, [_; 16]> = Default::default();
let mut addr = RefCell::new(addr);
loop {
// Otherwise, second `run` will panic.
RefCell::undo_leak(&mut addr);
if let Err(_) = run(&addr) { … }
}
} Alternatives… and why they were not chosen.
|
I have a very similar situation that warrants this functionality, including the need for I think that this comes up in any situation with the following constraints:
My simplified example looks something like the following: struct State {
wrapper: Wrapper,
counter: u64,
}
// Wrapper to make sure this isn't Clone / Copy.
struct DataType(u64);
struct FrameData<'sim> {
data: &'sim DataType
}
mod inner {
use std::cell::RefCell;
use super::*;
// This wrapper is needed so that nothing else can call `borrow` or `borrow_mut`.
pub struct Wrapper {
data: RefCell<DataType>
}
impl Wrapper {
pub fn new() -> Self {
Self {
data: RefCell::new(DataType(0))
}
}
pub fn simulate(&mut self, new_value: DataType) {
*self.data.borrow_mut() = new_value;
}
pub fn wrapper_frame_data<'frame, 'sim: 'frame>(&'sim self, frame_data: &mut Vec<FrameData<'frame>>) {
let guard = self.data.borrow();
let data: &DataType = &*guard;
// Transmute the data to the `sim lifetime, because we know that the state can't be modified during that lifetime.
let data: &'sim DataType = unsafe { std::mem::transmute(data) };
frame_data.push(FrameData {
data
});
}
}
}
use inner::Wrapper;
fn simulate(state: &mut State) {
state.counter += 1;
// This is the only place where the `wrapper` could be modified -- we need a &mut reference.
state.wrapper.simulate(DataType(state.counter));
}
fn state_frame_data<'frame, 'sim: 'frame>(state: &'sim State, frame_data: &mut Vec<FrameData<'frame>>) {
// `wrapper` cannot be mutated or dropped while `&'sim State` lives.
state.wrapper.wrapper_frame_data(frame_data);
}
fn render(state: &State) {
let mut frame_data = Vec::new(); // <-- This definitely lives less time than `State`.
state_frame_data(state, &mut frame_data);
for data in frame_data {
println!("{}", data.data.0);
}
}
fn main() {
let mut state = State {
wrapper: Wrapper::new(),
counter: 0,
};
loop {
simulate(&mut state);
render(&state);
}
} Using the API, all I'd need to do is make 2 changes: pub fn simulate(&mut self, new_value: DataType) {
RefCell::undo_leak(&mut self.data);
*self.data.get_mut() = new_value;
}
pub fn wrapper_frame_data<'frame, 'sim: 'frame>(&'sim self, frame_data: &mut Vec<FrameData<'frame>>) {
let guard = self.data.borrow();
let data: &DataType = &*guard;
// No more unsafe!
let data: &'sim DataType = Ref::leak(guard);
frame_data.push(FrameData {
data
});
} |
What is the current status of this feature? |
#68712 adds methods to convert
RefCell
guardsRef
/RefMut
into references with the lifetime of the underlying cell.The feature gate for the issue is
#![feature(cell_leak)]
.Unresolved Questions
MutexGuard
andRwLockReadGuard
/RwLockWriteGuard
?unsafe
methods be added to forcefully revert a leak?undo_leak
, would it make more sense not to return a reference (i.e., separate this fromget_mut
)?The text was updated successfully, but these errors were encountered: