-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Share allows for unsafe mutations in safe code #13438
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
I don't believe the |
That's true but before could specify that keys have to be |
Nominating. |
It's not clear that this is a 'memory safety' issue, but may be just a logic error. I'll also note that before removing |
There are a mix of problems/questions that should be addressed here:
We should resolve the questions above; they may need to be split off into subbugs. Accepted for 1.0, P-backcompat-lang |
It appears that Python is simliarly affected. The following creates a dictionary with keys from 0-9, and then "reverses" the keys in-place. class Foo:
def __init__(self, i):
self.i = i
def __eq__(self, other):
return self.i == other.i
def __hash__(self):
return self.i
def __repr__(self):
return 'Foo(%d)' % self.i
d = { Foo(i): i for i in range(0, 10)}
print('** Before **')
print(d)
print('d[1]: %s' % d[Foo(1)])
for k in d:
k.i = 9 - k.i
print('** After **')
print(d)
print('d[1]: %s' % d[Foo(1)])
(I don't know if this is memory-unsafe.) |
No, it may not necessarily always be a memory safety issue. But I remember several presentations where it was prominently stated that Rust's rules around references, mutability and aliasing prevented your typical iterator-invalidation bugs and ConcurrentModificationException scenarios. And I always found this to be very good argument for having the things the way they are. I feel that with Now, when I have a Well, this has become kind of a rant now |
The assumption you made was never true - you could, and still can, write the following code extern crate collections;
extern crate sync;
use collections::hashmap::HashMap;
use std::hash::Hash;
use std::io::File;
struct Evil {
ix: uint
}
impl Evil {
fn new(ix: uint, val: &[u8]) -> Evil {
let result = Evil { ix: ix };
result.mutate(val);
result
}
fn mutate(&self, val: &[u8]) {
let f = File::open(&Path::new(self.ix.to_str()));
f.write(val);
}
}
impl Eq for Evil {
fn eq(&self, other: &Evil) -> bool {
self.ix == other.ix
}
}
impl TotalEq for Evil {}
impl<S: Writer> Hash<S> for Evil {
fn hash(&self, state: &mut S) {
let _ = state.write_le_uint(File::open(&Path::new(self.ix.to_str()))
.read_to_end());
}
}
#[test]
fn test() {
let mut map = HashMap::new();
for i in range(0u, 1000u) {
map.insert(Evil::new(i, bytes!("good")), i);
}
// With `Share` you can mutate through immutable references ...
for k in map.keys() {
k.mutate(bytes!("evil"));
}
// ... leads to problems that Rust promises to prevent :(
for i in range(0u, 1000u) {
assert_eq!(map.find_copy(&Evil::new(i, bytes!("good"))), Some(i));
}
} Which breaks the invariant, with IO substituting for direct mutation. What you really want is pure functions - which Rust probably isn't going to get - which would (of course) prevent IO and dereference of Cells etc. |
I nominate this for removal; I don't see any way that addressing this could result in a backwards incompatible language change. Probably the biggest thing we could do is to reintroduce |
FWIW, I'm more relaxed about this issue now for a few reasons:
It would be nice if something like |
Closing for previously-stated reasons. |
Since the
Freeze
kind has been replaced withShare
it's possible to break things in safe code, such as mutating keys in a collection and thus invalidating the collection. WithFreeze
one was able to prevent this.Here is an example of how one can break the standard hashmap:
I think it might be a good idea to re-introduce
Freeze
(which could then implyShare
). Being able to specify that a type can be made deeply immutable is a very strong and a very useful guarantee. It's one of the reasons why Rust's&T
is much more useful than C++'sconst T&
. And as the above example shows,Share
cannot provide a replacement for this as it basically just means that access to the object is synchronized. (I'm not even sure thatShare
as it is defined now---allowing for mutation through & references---is a good idea; but that's just a gut feeling)The text was updated successfully, but these errors were encountered: