It might be nice to have some sort of pattern matching syntax for it, I suppose:
let [ref mut a..16, ref mut b..] = *c;
Meh. Sure is ugly. I'm not sure adding syntax would be worth it.Why does the author have to worry about aliasing in the first place? If they're dealing with POD types, there's no memory safety issue implicated by allowing overlapping slices (except possibly when accessing from multiple threads). No-alias guarantees can improve performance in many cases (though rustc currently isn't doing the best job conveying them to LLVM), but not always, and especially in something like a video codec, the author may prefer to manually optimize memory access patterns.
In my own code, I worked around this by using &[Cell<u8>] as an aliasable mutable buffer type. I had a function to cast to it from &mut [u8], as well as functions to read and write integers of various sizes by directly casting the pointer (although this may not be necessary, as LLVM can probably optimize the naive approach of calling .get()/.set() on each Cell individually). Simple enough, and I'm pretty sure it's safe, but I've never seen anyone else use this approach, and I don't know of any crates.io crates that implement it. Maybe I should write one...
There's the external https://crates.io/crates/alias and even an approved RFC for adopting it into std: https://github.com/rust-lang/rust/issues/43038 .
let (a, b) = c[..16..]; fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
let len = self.len();
let ptr = self.as_ptr() as *mut u8;
unsafe {
(from_raw_parts_mut(ptr, mid),
from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
}
} else {
slice_error_fail(self, 0, mid)
}
}
this is literally the src to .split_at_mut Make your own version. Rusts power isn't what it enables, it is what it removes, undefined behavior, false sharing, etc. Frankly, I am tired of C/C++ programmers complain about Rust. This is a non-starter. I am happy to convert the F#, Haskell, OCaml, Scala and Clojure devs to Rust. Systems programming isn't magic.Safe memory management without a garbage collector.
> If Ocaml solves the problem why Rust?
Ocaml doesn't solve every problem any more than Rust does. Rust may solve a subset of the problems people use the listed languages for better than those languages, so it might be beneficial for people to use it in those cases.
> Rust was made to solve the problem Ada solves.
Not all solutions have the same efficiency, or are as easy to understand. Rust aims to enforce safety at compile time to eliminate runtime safety checks where possible. Ada performs many safety checks at runtime, which introduces a performance cost. This does allow Ada to provide some additional safety checks though (such as limiting a type/subtype to a range of values).
I've only looked into Rust briefly, but I thought this was the point of unsafe blocks? You can roll your own multi-dimensional array data structure using unsafe blocks if you're certain what you're doing is actually safe.
That said, you can create raw and dereference raw pointers, which lets you to do the above.
With a large single array of 100 elements, but divided into 10 10 element pieces by convention, a slice of elements might be accessed by getting the elements array[10a+b] through array[10a+c]. If we also want a slice from the next segment, that would be array[10(a+1)+b] through array[10(a+1)+c].
With a multidimensional array, of 10 bounds checked segments of 10 elements, we could access by array[a][b] through array[a][c] and array[a+1][b] through array[a+1][c]. If access is bounds checked, or the compiler can prove the values are within the sizes, then the multidimensional version provably has no overlap.
Or am I missing something here?
I was actually thinking of this when I wrote, but the opposite case. A lot of compilers already know about that, and exploit it to their benefit (and sometimes the programmers dismay[1]). Overflow semantics matter here.
That said, it's looks like an interesting area for Rust. I'm sure it's already been discussed to death somewhere in the community. :)
Integrating this sort of thing into the type system "properly" (as in, actually part of the language and doing all of what I think Animats wants) basically means going a long way towards full dependent typing.
[1]: behaviour on invalid code, such as C relying on signed integer overflow, is unspecified/undefined. Languages like, say, Rust and Haskell generally try to guarantee the optimiser never gets invalid code by flagging such instances at compile time, unlike most/all C and C++ compilers.
Here's a doc on how to write such a function yourself, and includes the (actual) source code for split_at_mut:
https://doc.rust-lang.org/nomicon/borrow-splitting.html
As others have mentioned, there's nothing special about the standard library in this regard; you can just write the method yourself and it'll work equally well. (The standard library is moderately special in that it gets to use nightly features because dealing with breaking changes is easy for code that literally lives inside Rust's own source repository, but it's not special in terms of its ability to use unsafe code or anything.)
(There's no reason to avoid safe functions that happen to have unsafe implementations. For instance, the standard-library implementation of indexing a slice, as in b"hello"[3], is just a bounds check plus an unsafe dereference.)
It would be easier if Rust had real multidimensional arrays. If the compiler knows about multidimensional arrays, some additional optimizations are possible. For example, if you want to borrow two different rows, that's fine as long as the row subscript is different.