Parameters can be passed to functions and modified:
fn set_42(int: &mut i32) {
*int += 42;
}
fn main() {
let mut int = 0;
set_42(&mut int);
println!("{:?}", int);
}
Output:
42
Changing the code to use a slice fails with a whole bunch of errors:
fn pop_front(slice: &mut [i32]) {
*slice = &{slice}[1..];
}
fn main() {
let mut slice = &[0, 1, 2, 3][..];
pop_front(&mut slice);
println!("{:?}", slice);
}
Output:
error[E0308]: mismatched types
--> src/main.rs:2:14
|
2 | *slice = &{ slice }[1..];
| ^^^^^^^^^^^^^^^
| |
| expected slice `[i32]`, found `&[i32]`
| help: consider removing the borrow: `{ slice }[1..]`
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
--> src/main.rs:2:5
|
2 | *slice = &{ slice }[1..];
| ^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[i32]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: the left-hand-side of an assignment must have a statically known size
If we try using a mutable slice (which isn't what I really want; I don't want to modify the values within the slice, I just want to modify the slice itself so it covers a smaller range of elements) and a mutable parameter, it has no effect on the original slice:
fn pop_front(mut slice: &mut [i32]) {
slice = &mut {slice}[1..];
}
fn main() {
let mut slice = &mut [0, 1, 2, 3][..];
pop_front(&mut slice);
println!("{:?}", slice);
}
Output:
[0, 1, 2, 3]
Is there a way to modify a slice that's a function parameter? I don't want to modify the elements within the slice; I just want to modify the range of the slice itself so it becomes a smaller "sub-slice".
Slices in Go have a base array and are passed by reference, not value. We just pass the slice reference to the function, then change it, and it changes the outer slice.
In Go language, you are allowed to pass a slice to a function, means the function gets the copy of the slice. Explanation: In the above example, we have a slice named as slc. This slice is passed in the myfun() function.
As we already know that slice is dynamic, so the new element can be appended to a slice with the help of append() function. This function appends the new element at the end of the slice.
Golang default is pass by Value. There is no pass by reference like in c++. Slices when its passed it's passed with the pointer to underlying array.
Using part of Francis Gagné's answer (I didn't think of trying &mut &
), I was able to get it working without using unsafe
code:
fn pop_front(mut slice: &mut &[i32]) {
*slice = &slice[1..];
}
fn main() {
let mut slice = &[0, 1, 2, 3][..];
pop_front(&mut slice);
println!("{:?}", slice);
}
Output:
[1, 2, 3]
As others have said, the core idea here is to take a &mut &... [T]
(where ...
is mut
or empty) and read/write to the internal slice. The other answers demonstrate it is possible for &mut &[T]
in safe code, and possible for &mut &mut [T]
with unsafe code, but they don't explain why there's the difference... and &mut &mut [T]
is possible with safe code too.
In explicit-lifetime terms, the nested reference is something like &'a mut &'b ... [T]
for some lifetimes 'a
and 'b
, and the goal here is to get a &'b ... [T]
, slice it and write that into the &'a mut
.
For &'a mut &'b [T]
, this is easy: &[T]
is copy, so writing *slice = &slice[1..]
will effectively copy the &'b [T]
out of the &mut
and then, later, overwrite the existing value with the shorter one. The copy means that one literally gets a &'b [T]
to operate with, and so there's no direct connection between that and the &'a mut
, and hence it is legal to mutate. It is effectively something like
fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) {
// *slice = &slice[1..] behaves like
let value: &'b [i32] = *slice;
*slice = &value[1..]
}
(I've labelled the lifetimes and annotated the type to tie into my explanation, but this is not required for the code to work.)
For &'a mut &'b mut [T]
things are a little trickier: &mut [T]
cannot be copied: dereferencing won't copy, it will reborrow to give a &'a mut [T]
i.e. the slice has a lifetime that is connected to the outer &'a mut
, not the inner &'b mut [T]
. This means the sliced reference has a shorter lifetime than the type it is trying to overwrite, so it's invalid to store the slice into that position. In other words:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'a mut [i32] = &mut **slice;
*slice = &mut value[1..] // error
}
The way to do this safely for &'a mut &'b mut [T]
is to get the internal slice out of the reference with that 'b
lifetime. This requires keeping track of the "one owner" rule, doing no borrowing, and the best function for this sort of ownership manipulation is mem::replace
. It allows us to extract the inner &'b mut [T]
by swapping it with some placeholder, which we can then overwrite with the short version. The best/only placeholder is an empty array: writing &mut []
can be a &'c mut [X]
for any type X
and any lifetime 'c
, since there's no data to store and so nothing needs initialisation, and no data will ever become invalid. In particular, it can be a &'b mut [T]
:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'b mut [i32] = mem::replace(slice, &mut []);
*slice = &mut value[1..]
}
Since &mut[T]
implements Default
, we can also use mem::take
:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'b mut [i32] = mem::take(slice);
*slice = &mut value[1..]
}
(As above, I've made things more explicit than necessary.)
See also:
If you need to modify an immutable slice, see Cornstalks's answer.
You cannot modify a mutable slice in safe Rust. When you take a subslice of a mutable slice, you effectively borrow from the original slice. This means that the subslice must not outlive the original slice.
You want something that looks like this:
fn pop_front(slice: &mut &mut [i32]) {
*slice = &mut slice[1..];
}
but the subslice slice[1..]
is only valid until the end of the function, and which point the borrow will end and the original slice (the slice
parameter) will be usable again.
We can use some unsafe
code to construct manually the slice we want:
use std::slice;
fn pop_front(slice: &mut &mut [i32]) {
let ptr = slice.as_mut_ptr();
let len = slice.len();
*slice = unsafe { slice::from_raw_parts_mut(ptr.offset(1), len - 1) };
}
fn main() {
let mut slice = &mut [0, 1, 2, 3][..];
pop_front(&mut slice);
println!("{:?}", slice);
}
playground
This program outputs:
[1, 2, 3]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With