Skip to content
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

Comments: Learn Unsafe Rust from My Mistakes #49

Open
geo-ant opened this issue Jul 24, 2023 · 8 comments
Open

Comments: Learn Unsafe Rust from My Mistakes #49

geo-ant opened this issue Jul 24, 2023 · 8 comments

Comments

@geo-ant
Copy link
Owner

geo-ant commented Jul 24, 2023

Comments for this article go here :)

@tbu-
Copy link

tbu- commented Jul 25, 2023

I went through a similar journey when I first started using Rust. In fact, I contributed the original map_in_place to the standard library (rust-lang/rust#16853). Back then, it was a lot easier to add methods to the standard library. ^^

I'm happy to hear that this function is now unnecessary as it now works for somewhat arbitrary iterator chains, as proposed by @alexcrichton on the original PR: rust-lang/rust#15302 (comment).

The optimization you allude to happens in the following file: library/alloc/src/vec/in_place_collect.rs. It took me a couple of minutes to find. :) It was apparently added in rust-lang/rust#70793.

Thanks for your interesting blog post. :)

@geo-ant
Copy link
Owner Author

geo-ant commented Jul 26, 2023

@tbu- Oh cool, it's always nice to hear that other devs shared some of the struggle. Thanks for taking the time to find the sources and provide more context.

@GoldsteinE
Copy link

Hi!

I think this example is misleading:

let mut x = 10;
let p1: *mut i32 = &mut x;
let p2: *mut i32 = &mut x;
unsafe {
  // ...
}

The text around it seems to imply that we created two usable pointers, but p1 is actually invalidated by the second mutable borrow and isn’t usable under Stacked Borrows (which can be easily validated with Miri). I think it’s valid under Tree Borrows, but it’s not even stable in Miri, so it’s far from being a commonly accepted memory model for Rust.

@geo-ant
Copy link
Owner Author

geo-ant commented Jul 31, 2023

Hi!

I think this example is misleading:

Hey @GoldsteinE , good point. I should just be able to replace the second line by let p2 = p1 and still have a valid p1, since pointers are clone, right?

@GoldsteinE
Copy link

Yeah, that should work (the relevant part is that they’re Copy, not Clone tho).

@geo-ant
Copy link
Owner Author

geo-ant commented Aug 1, 2023

Yeah, that should work (the relevant part is that they’re Copy, not Clone tho).

Oh yeah, sorry that's what I meant to say...

@MalbaCato
Copy link

Small nitpic:

As leaking destructors isn't unsound in rust, the "first" version of the function is sound (or at least panic safe). This can be shown by writing a semantically equivalent function using safe rust only, by forcing everything to happen inside ManuallyDrops:

fn safe_map_in_place<T, U, F>(v: Vec<T>, f: F) -> Vec<U>
where
    F: Fn(T) -> U,
{
    let mut v = ManuallyDrop::new(v.into_iter().map(f));
    let mut u = ManuallyDrop::new(Vec::new());
    u.extend(&mut *v);
    ManuallyDrop::into_inner(u)
}

This behaviour is certainly incorrect due to being surprising and would be easy to misuse in the presence of other unsafe code that relied on destructors being ran, but clearly safe while unwinding.

Miri refuses to accept this wasteful attitude towards owned recourses, but with a little bit of trickery (or turning off memory leak tracking), it too sees that nothing bad is actually going on.

@geo-ant
Copy link
Owner Author

geo-ant commented Nov 30, 2023

@MalbaCato hey thanks for the comment, I have questions. First if all I think you're right that soundness does not guarantee that destructors are run. I should have stated that more clearly.

The other thing is that I am staring at your example (which is beautiful btw), but I don't understand why it would guarantee in place mapping? Meaning why would vector u reuse the memory allocated for vector v. Not saying that isn't the case, just saying I don't understand what about the code would guarantee it. Could you explain it a bit more?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants