I just found that (again...) the types in the vec module were changed and they are now unsound with respect to mutability. I just wanted to send a brief e-mail to try and explain why things are the way they are. In general, we are too loose with respect to mutability when it comes to unsafe pointers, and we make excessive use of reinterpret_cast, so the compiler can't help us catch mistakes. We then end up with "safe" external interfaces that are in fact unsafe. Prime among them, `vec::each`, which is currently written:

    fn each(v: &[const T], f: fn(&&v: T) -> bool) { ... }

The reason that this is unsafe is because the callback `f` expects an *immutable reference* to `T`. This means, in practice, it expects a pointer to `T` that it can rely upon not to be overwritten. But the vector we are iterating over, `v`, is not guaranteed to have *immutable* contents, only *const* contents (that is, contents which may or may not be immutable).

The reason that this function compiles at all is because `as_buf()` has incorrect types:

    fn as_buf(v: &[const T], f: fn(*T, uint));

Here you see that `as_buf` takes a vector with `const` contents and produces a pointer `*T` that is supposedly pointing at *immutable* memory. But of course it's not, not really, it's pointing at const memory. The correct type of as_buf is:

    fn as_buf(v: &[T], f: fn(*T, uint));

To accomodate `&[const T]` or `&[mut T]`, there are other functions:

    fn as_const_buf(v: &[const T], f: fn(*const T, uint));
    fn as_mut_buf(v: &[mut T], f: fn(*mut T, uint));

All of these functions exist (I put them in a while back), but `as_buf()` is over-used because it is incorrectly typed.

In practice, what these various rules mean is that basically what winds up happening is that almost all `vec` functions only work on `&[T]`. This is generally ok for two reasons. First, I would say that the type `~[mut T]` is almost always not what you want. Due to inherited mutability, you probably want to replace code like this:

    let x = ~[mut 1, 2, 3];
    x[0] += 1;

with

    let mut x = ~[1, 2, 3];
    x[0] += 1;

In fact, your variable was probably `mut` to begin with, since you probably had to grow up your array.

But, even if you continue to use `~[mut T]` for some reason, such a type can in fact *still be coerced to &[T]*, so long as the fn using the slice is pure. Most places in vec that today are written with `&[const T]` basically just want an array they can read. It is better to declare such fns as pure and require a `&[T]`.

In fact, I think we should probably just remove the notion of a `~[mut T]` array type. Mutability qualifiers should only appear on variable names, field names, and after an `&` or `*` sigil. But that's for another e-mail.


Niko
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to