[英]How can I modify a slice that is a function parameter?
參數可以傳遞給函數並修改:
fn set_42(int: &mut i32) {
*int += 42;
}
fn main() {
let mut int = 0;
set_42(&mut int);
println!("{:?}", int);
}
輸出:
42
更改代碼以使用切片失敗並出現大量錯誤:
fn pop_front(slice: &mut [i32]) {
*slice = &{slice}[1..];
}
fn main() {
let mut slice = &[0, 1, 2, 3][..];
pop_front(&mut slice);
println!("{:?}", slice);
}
輸出:
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
如果我們嘗試使用可變切片(這不是我真正想要的;我不想修改切片中的值,我只想修改切片本身,使其覆蓋更小的元素范圍)和可變切片參數,它對原始切片沒有影響:
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);
}
輸出:
[0, 1, 2, 3]
有沒有辦法修改作為函數參數的切片? 我不想修改切片內的元素; 我只想修改切片本身的范圍,使其成為更小的“子切片”。
正如其他人所說,這里的核心思想是采用&mut &... [T]
(其中...
是mut
或空)並讀取/寫入內部切片。 其他答案表明&mut &[T]
在安全代碼中是可能的,並且&mut &mut [T]
在不安全代碼中是可能的,但他們沒有解釋為什么存在差異......並且&mut &mut [T]
是可能的也有安全碼。
在顯式生命周期術語中,嵌套引用類似於&'a mut &'b ... [T]
對於某些生命周期'a
和 ' b
,這里的目標是獲得&'b ... [T]
,將其切片並將其寫入&'a mut
。
對於&'a mut &'b [T]
,這很簡單: &[T]
是復制,所以寫*slice = &slice[1..]
將有效地將&'b [T]
復制出&mut
然后,稍后,用較短的值覆蓋現有值。 副本意味着從字面上得到一個&'b [T]
來操作,因此它和&'a mut
之間沒有直接聯系,因此變異是合法的。 它實際上類似於
fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) {
// *slice = &slice[1..] behaves like
let value: &'b [i32] = *slice;
*slice = &value[1..]
}
(我已經標記了生命周期並注釋了類型以配合我的解釋,但這不是代碼工作所必需的。)
對於&'a mut &'b mut [T]
事情有點棘手: &mut [T]
不能被復制:解引用不會復制,它會重新借給&'a mut [T]
即切片有一個生命周期連接到外部&'a mut
,而不是內部&'b mut [T]
。 這意味着切片引用的生命周期比它試圖覆蓋的類型短,因此將切片存儲到該位置是無效的。 換句話說:
fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) {
let value: &'a mut [i32] = &mut **slice;
*slice = &mut value[1..] // error
}
為&'a mut &'b mut [T]
安全地執行此操作的方法是從具有該'b
生命周期的引用中取出內部切片。 這需要跟蹤“一個所有者”規則,不進行借用,這種所有權操作的最佳功能是mem::replace
。 它允許我們通過與一些占位符交換它來提取內部&'b mut [T]
,然后我們可以用短版本覆蓋它。 最好/唯一的占位符是一個空數組:對於任何類型X
和任何生命周期'c
,寫入&mut []
可以是&'c mut [X]
,因為沒有數據要存儲,所以不需要初始化,也沒有數據會永遠無效。 特別地,它可以是 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..]
}
由於&mut[T]
實現了Default
,我們也可以使用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..]
}
(如上所述,我已經使事情變得比必要的更明確。)
也可以看看:
如果您需要修改不可變切片,請參閱 Cornstalks 的回答。
您不能在安全的 Rust 中修改可變切片。 當您獲取可變切片的子切片時,您實際上是從原始切片中借用了。 這意味着子切片不能超過原始切片。
你想要看起來像這樣的東西:
fn pop_front(slice: &mut &mut [i32]) {
*slice = &mut slice[1..];
}
但是 subslice slice[1..]
僅在函數結束前有效,借用將結束並且原始切片( slice
參數)將再次可用。
我們可以使用一些unsafe
代碼來手動構造我們想要的切片:
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);
}
該程序輸出:
[1, 2, 3]
使用Francis Gagné 的部分答案(我沒想過嘗試&mut &
),我能夠在不使用unsafe
代碼的情況下使其工作:
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);
}
輸出:
[1, 2, 3]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.