-
Notifications
You must be signed in to change notification settings - Fork 58
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
Aliasing rules for Box<T> #258
Comments
Thank you! The use-case I have is in https://github.com/jonhoo/rust-evmap, which provides concurrent read-only access to a map by keeping two copies of the map, one used by readers, and one used by the (singular) writer. Since the two maps generally hold the same keys and values, it's wasteful to duplicate all the keys and values between the maps. Instead, (my hope is) the values can be aliased so long as they are only either accessed by multiple readers, or by a single writer, at a time. Since aliasing is unsafe, While the value could be a raw pointer, that would make the ergonomics of using |
Could you give a concrete self-contained piece of code demonstrating what you mean by "aliasing a |
@jonhoo I don't think there is any way to make the aliased I think you could make |
@RalfJung The relevant code is this bit, which I hadn't looked at in a while and just realized is horribly not okay as it casts from fn shallow_copy(self: &Box<T>) -> ManuallyDrop<Box<T>> {
ManuallyDrop::new(Box::from_raw(&**self as *const _ as *mut _))
} But yes, your example would be a better way to illustrate the same thing. And my guess is you'll tell me that that is still casting from a @digama0 Hmm, yes, I think your suggestion could probably work. It would still make it a little painful to actually generate the value to insert into the map, but an additional method might be able to help there (something like I was definitely hoping there'd be a way for me to just use |
Here's a toy version of the |
Another way to look at the problem is that |
Currently in lccc, the pointer field of |
Basically, yeah. I am curious, if all I can certainly imagine rules whereby We'll know a lot more about this design space once there is a concrete proposal that solves #133. Right now, all I can do is speculate.
I agree with this also just from a matter of simplicity -- not having yet another set of rules would be preferrable, I think. |
@RalfJung Actually, no, I don't think I'd expect I think maybe what's going on in my head is that the |
|
Also you can safely go from |
Well, that would fall under the "safety invariant", would it not? Though I
do agree with the other point. I have a hard time seeing how
ManuallyDrop<T> does not "own" a T.
…On Fri, Nov 27, 2020 at 15:46 bjorn3 ***@***.***> wrote:
Also you can safely go from ManuallyDrop<Box<T>> to &mut T, so any
aliasing rules that apply to &mut T must also apply to
ManuallyDrop<Box<T>>.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#258 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABGLD2ZE56YCR3D6JQCZF2LSSAF3PANCNFSM4T7NZQJA>
.
|
In that case @jonhoo it may be better to use |
Are the aliasing rules around Box just the rules for |
I don't believe Stacked Borrows special cases Unique, though I believe Unique happens to be an (unstable) implementation detail of how it special cases Box. As shown previously, my implementations treats it almost the same, adding the |
Indeed it does not. That is an extension I hope I will be able to support eventually, and at that point I think
So, what about |
Hehe, so, to be clear, I'm not really trying to say that my opinion here makes sense, just trying to explain my thinking for why I believed that this would work. Presented with the logic for why that's not the case, I agree with it, I'm just hoping that the explanation might help highlight a likely common misunderstanding that this aliasing behavior will lead to. @bjorn3 Your point that @RustyYato So, I'm hesitant to use @RalfJung |
A related question: do the same aliasing rules then apply to other owner heap types, like |
I went down a deep deep rabbit hole trying to find a way to avoid violating the aliasing requirement for |
That's fair, however I see it more as "the normal validity peoperties don't necessarily apply", either because it's uninitialized or because you've done something special, like
Don't you have unsafe mutability? You're sharing a
Yes, but that's normal Rust, the issue right now is tou have UB even if you don't touch the box. Using
I'm fairly certain that |
@RustyYato how would you expect @jonhoo
When the length is 0, the inner pointers can dangle, so it's a bit less clearcut. That being said, it's also better to be careful about these things too. For instance, since a
|
I was thinking that |
My expectation is that
|
Also, I'm curious how a |
Yes, I think you're all right that I still feel like |
Why you think that is exactly what I am trying to figure out by probing your intuition, so thanks for the response. :) Unfortunately, I do not understand the reasoning though. What do you mean by "an exclusive borrow has been taken out on the T"? You seem to say that a mutable reference still exists in some sense, even when it is inside
I'd say
Ah, good call. I don't think we have a clear consensus yet on what |
Am I smelling a More seriously, every time I read conversations about aliasing rules it sounds like we all want both fine grained control and a lot of optimizations, leading us to a lot of different types: |
❤️
Yes, I suppose it is. I think maybe this boils down to "I don't feel like keeping two
I'm hesitant to have a wrapper just for the pointer case. What's "nice" about
I feel like |
Okay, so you do not expect a symmetry between
It could still be the case that the "validity invariant" of |
I was thinking about giving |
FWIW, that's what I find most consistent, albeit maybe a bit subtle. I guess a good way to "show" what is subtle or tricky is to find an unintuitive code snippet, or a pair of code snippets whereby one is UB and the other is not, despise "intuition" telling they are equivalent (I like using implicit drop vs. explicit It turns out I posted such a pair of snippets in a previous comment, but such snippets go in the other direction: that Having: struct CString {
inner: Box<[u8]>, /* never empty */
} then:
And I personally feel like the following should be fine too:
However, IIUC, that Indeed, that would assert that the |
should the return types of those functions be |
The key difference is that
If aliasing requirements propagate through |
added a strings.rs regression test case for potential future UB This PR adds a regression test for the aliasing rules of a `Unique<T>` pointer. At the time of writing this test case, Miri does not treat `Unique<T>` pointers as a special case, these are treated like any other raw pointer. However, there are existing Github issues which may lead to `Unique<T>` becoming a special case through asserting unique ownership over the pointee: - rust-lang/unsafe-code-guidelines#258 - rust-lang/unsafe-code-guidelines#262 In the new test case, the calls to `String::remove` and `String::insert[_str]` follow code paths that would trigger undefined behavior in case `Unique<T>` would ever assert semantic ownership over the pointee. Internally, these methods call `self.vec.as_ptr()` and `self.vec.as_mut_ptr()` on the vector of bytes that are backing the `String`. That `Vec<u8>` holds a `Unique<u8>` internally. The second call to `Vec::as_mut_ptr(&mut self)` would then invalidate the pointers derived from `Vec::as_ptr(&self)`. Note that as long as `Unique<T>` is treated like any other raw pointer, this test case should pass. It is merely here as a canary test for potential future undefined behavior.
I just realized I opened #326 as a duplicate of this... let's keep the newer one open since it probably has more up-to-date information. Also, on this thing I wrote in my last post here almost 3 years ago...
That's exactly what rust-lang/rfcs#3336 proposes. :) |
What should the aliasing rules for Box be? Stacked Borrows says they are the same as
&mut T
. (With the exception that no protectors are being added, so aBox<T>
may actually be deallocated while a function is running. However, if we adopt #252, then even this difference will probably disappear.)However, @jonhoo has a usecase that is in conflict with this. @jonhoo maybe you could briefly summarize what that is about? In particular I am wondering if you think you should be able to pull the same tricks with
&mut
-- so far I treated it as a given that bothBox
and&mut
should require uniqueness in pretty much the same way.The text was updated successfully, but these errors were encountered: