如何修改作为函数参数的切片? [英] How can I modify a slice that is a function parameter?

查看:40
本文介绍了如何修改作为函数参数的切片?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

参数可以传递给函数并修改:

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);
}

输出:

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);
}

输出:

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);
}

输出:

[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".

推荐答案

正如其他人所说,这里的核心思想是采用 &mut &... [T](其中...mut 或空)并读/写到内部切片.其他答案表明 &mut &[T] 在安全代码中是可能的,并且 &mut &mut [T] 与不安全代码是可能的,但他们没有解释为什么会有不同......而且 &mut &mut [T] 也可以使用安全代码.

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.

在显式生命周期术语中,嵌套引用类似于 &'a mut &'b ... [T] 对于某些生命周期 'a 和'b,这里的目标是得到一个 &'b ... [T],将它切片并将其写入 &'amut.

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.

对于 &'a mut &'b [T],这很简单:&[T] 是复制,所以写 *slice= &slice[1..] 将有效地将 &'b [T]&mut 复制出来,然后,稍后覆盖现有值与较短的值.副本意味着从字面上得到一个 &'b [T] 来操作,所以它和 &'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.)

对于 &'a mut &'b mut [T] 事情有点棘手:&mut [T] 不能被复制:解引用赢了't 复制,它将重新借给一个 &'a mut [T] 即切片有一个生命周期,它连接到 outer &'amut,而不是内部的 &'b mut [T].这意味着切片引用的生命周期比它试图覆盖的类型短,因此将切片存储到该位置是无效的.换句话说:

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
}

&'a mut &'b mut [T] 安全地执行此操作的方法是使用 'b 从引用中取出内部切片> 一生.这需要跟踪一个所有者".规则,不借用,这种所有权操作的最佳功能是 mem::replace.它允许我们通过与一些占位符交换它来提取内部 &'b mut [T] ,然后我们可以用短版本覆盖它.最好/唯一的占位符是一个空数组:写 &mut [] 可以是任何类型 X&'c mut [X]code> 和任何生命周期 'c,因为没有数据要存储,所以不需要初始化,并且没有数据会变得无效.特别是,它可以是一个 &'b mut [T]:

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..]
}

由于 &mut[T] 实现了 Default,我们也可以使用 mem::take:

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.)

另见:

这篇关于如何修改作为函数参数的切片?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆