当将一个可变数组引用传递给一个也接受可变数组引用的函数时,为什么不需要声明 &mut?

4

我正在尝试为一个i32数组实现快速排序算法。我的问题是,在将数组传递给函数时,使用&mut存在明显的不一致性。我的代码:

fn main() {
    let mut my_array = [5, 2, 8, 7];
    let high = my_array.len() - 1;
    quick_sort(&mut my_array, 0, high);
    print!("{:?}", my_array);
}

fn quick_sort(array: &mut [i32], low: usize, high: usize) {
    let pivot_idx = partition(array, low, high);

    // Separately sort elements before partition and after partition
    if pivot_idx > low {
        quick_sort(array, low, pivot_idx - 1);
    }
    if pivot_idx < high {
        quick_sort(array, pivot_idx + 1, high);
    }
}

fn partition(array: &mut [i32], low: usize, high: usize) -> usize {
    let pivot = array[high]; // pivot
    let mut i = low;

    for j in low..high {
        // If current element is less than or equal to pivot
        if array[j] <= pivot {
            let swapper_variable = array[i];
            array[i] = array[j];
            array[j] = swapper_variable;
            i += 1; // increment index of smaller element
        }
    }

    let swapper_variable = array[i];
    array[i] = array[high];
    array[high] = swapper_variable;
    return i;
}

我的代码工作了,但是它应该吗?在main函数中,我使用quick_sort(&mut my_array, 0, high)调用快速排序,但是quick_sort函数内部的递归调用使用quick_sort(array, low, pivot_idx - 1)而没有使用&mut。
此外,在quick_sort中,我还使用partition(array, low, high)调用了partition函数,但是partition函数需要一个可变数组作为参数,而我在调用时没有指定&mut。如果我尝试在partition函数调用中添加&mut,则代码将无法编译: error[E0596]: cannot borrow `array` as mutable, as it is not declared as mutable --> src/main.rs:9:31 | 8 | fn quick_sort(array: &mut [i32], low: usize, high: usize) { | ----- help: consider changing this to be mutable: `mut array` 9 | let pivot_idx = partition(&mut array, low, high); | ^^^^^^^^^^ cannot borrow as mutable 所以似乎存在一些混淆,即数组本身是否可变,还是指向该数组的指针(?)是否可变。但是在我的看来,我应该能够以与从main函数最初调用quick_sort相同的方式调用quick_sort内的函数。据我所知,强制在函数调用中显式放置&mut的目的是这样做可以轻松地看到被调用的函数可能修改参数,但是quick_sort内部的调用没有表明它们会修改数组。
代码演示在此:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=62813cd1bd23d8b1a36d6126d4305800
1个回答

5
在您的主函数中,my_array[i32] 类型,但 quick_sort 需要 &mut [i32] 类型,这就是为什么您需要使用 &mut my_array 的原因;但是在您的 quick_sort 函数中,参数 array 已经是 &mut [i32] 类型,这就是为什么您可以直接将它传递给 partition 和递归的 quick_sort 调用。这正是您应该遵循类型标注调用函数的方式。
fn main() {
    let mut my_array = [5, 2, 8, 7];         // [i32]
    quick_sort(&mut my_array, 0, high);      // &mut my_array gives type &mut [i32]
}

fn quick_sort(array: &mut [i32], low: usize, high: usize) {
    let pivot_idx = partition(array, low, high);    
    // notice array is already of type &mut [i32], which is required by partition function,
    // you shouldn't take another reference on top of it
    ...
}

2
只是挑剔一下:在 main 函数中,my_array 的类型是 [i32; 4],然后 &mut my_array&mut [i32; 4],它强制转换为 &mut [i32] - mcarton
好的,将 &mut [i32] 视为一个独立的类型有助于我在脑海中理解它。到目前为止,我非常喜欢 Rust,但我发现魔法和非魔法的混合令人困惑。为什么我可以在借用类型上调用点运算符,但是在将其传递到函数中时,我必须将其视为不同的类型?我希望借用值像原始值一样处理,只是借用限定符意味着它没有被复制并且不会在该作用域内过期。然后就可以使用关键字/类型转换来访问指针本身。这难道不比这种半吊子的指针抽象更直观吗? - Gabe Nodarse
调用借用类型上的点运算符并将其传递到函数中。好的,你提到了两个操作,但你没有很好地定义它们。基本上,第二个操作更通用,适用于任何静态语言,即您应始终考虑类型。您可以在借用类型上调用点运算符的原因是由于Rust的自动解引用:https://dev59.com/Zl4b5IYBdhLWcg3w7Fgv。 - Psidom
我希望借用的值能像原始值一样被处理。我认为自动解引用在某种程度上试图从使用角度实现这个目的。但这并不意味着我们可以忽略它们之间的差异,因为那将会打败 Rust 强制执行的借用检查系统的所有目的,不是吗? - Psidom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接