簡體   English   中英

如何修改作為函數參數的切片?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM