-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Finally blocks for safer, faster, and clearer unsafe code #1010
Comments
@pnkfelix this may be relevant to your drop flag work |
What about a #![feature(unsafe_destructor)]
use std::ops;
pub struct Finally<T: FnMut() -> ()>(Option<T>);
impl<T: FnMut() -> ()> Finally<T> {
fn new(t: T) -> Finally<T> {
Finally(Some(t))
}
}
#[unsafe_destructor]
impl<T: FnMut() -> ()> ops::Drop for Finally<T> {
fn drop(&mut self) {
self.0.take().unwrap()();
}
} Then the example looks like: fn sift_up(&mut self, start: usize, mut pos: usize) {
unsafe {
let new = Some(ptr::read(&self.data[pos]));
let finally = Finally::new(|| ptr::write(&mut self.data[pos], new.take().unwrap());
while pos > start {
let parent = (pos - 1) >> 1;
if new.as_ref().unwrap() <= self.data[parent] { break; }
ptr::copy_nonoverlapping(&mut self.data[pos], &self.data[parent], 1);
pos = parent;
}
}
} Has the advantage that you get good control over when the "finally" should be invoked, but the disadvantages that a) it must use the |
@aidancully Looks similar to the now-deprecated |
@kennytm Yes, you're right, and what I wrote won't work either since let new = ptr::read(&self.data[pos]);
// `env` is the dtor `FnOnce` passed to the `finally` function.
(|env| {
...
// allow borrows from `env`'s captured environment
if env.new <= self.data[parent] { break; }
}).finally(|| ptr::write(&mut self.data[pos], new)); This would rely on blanket |
See also Jonathan Blow's
|
We do have exception safety problematics. Rust has exceptions but no exception handling, that sounds like a match to |
@gankro We can do that in Rust with an "owning scope guard",, but it can only offer Deref & DerefMut access to its protected value. |
@gankro That I don't think we need to introduce I think it's fine to make this construct only available inside of All that said, I'm not quite sure about the syntax. Having a |
I believe that in this specific case of |
👍 I like the idea, but I'm not sure about thee syntax. Is it necessary to introduce a new keyword? |
macro_rules! finally {
($contents:block) => {
struct A;
impl Drop for A {
fn drop(&mut self) {
{ $contents };
}
}
let a = A;
};
}
fn main() {
finally! {{
println!("hello, world!");
}}
println!("but first...");
} I tried to allow single braces by using EDIT: this won't work if you try to access data in the surrounding scope, but all you need to do to fix that is make A take a closure, wrap EDIT2: possibly a good idea to somehow wrap everything in a RefCell so that it doesn't interfere with semantics, or do it some other way since we know values won't be used until the end of the method. Either way, this is definitely something that I would rather see done with a macro or plugin. |
BinaryHeap currently has the following impl of sift_up that relies on the drop flag and LLVM being smart to avoid some swaps while being panic-safe:
This code is quite unclear, and relies on a lot of things going right. However if we had a
finally
block, we could do the following:Which clearly captures the actual semantics we want. This functionality can be emulated by creating a struct with a drop impl, but it requires "weakly capturing" all the values through
*const
's. However that is noisy, cumbersome, confusing, and needlessly unsafe. A first class finally block can be verified to correctly run no matter where unwinding occurs (possibly through verifying that nothing it "closes" over is ever conditionally moved out).I've constructed similar code while working on some trusted_len iterator problems (using the *const weak closing drop impl).
It would be fine with me if finally was considered
unsafe
, since its value to me is for cleanly failing in transient unsafe states.I am logging this as only an issue because I don't have the knowledgebase to flesh out the precise rules, and because there's no way this can land for 1.0.
The text was updated successfully, but these errors were encountered: