生锈报告在模板化代码时不能脱离取消引用

Sha*_*mar 2 generics rust

Rust中的以下代码编译正常:

pub fn insertion_sort(data : &mut [int]){
  let n  = data.len();
  for j in range(1, n){
    let key = data[j];
    // we insert data[j] into the sorted sequence 
    //data[0...j-1]
    let mut i = j -1;
    while data[i] > key{
        data[i + 1]  = data[i];
        if i == 0{
            break;
        }
        i -= 1;
    }
    data[i] = key;
  }
}
Run Code Online (Sandbox Code Playgroud)

但是那一刻,我介绍了泛型如下:

pub fn insertion_sort<T : Ord>(data : &mut [T]){
  let n  = data.len();
  for j in range(1, n){
    let key = data[j];
    // we insert data[j] into the sorted sequence 
    //data[0...j-1]
    let mut i = j -1;
    while data[i] > key{
        data[i + 1]  = data[i];
        if i == 0{
            break;
        }
        i -= 1;
    }
    data[i] = key;
  }
}
Run Code Online (Sandbox Code Playgroud)

编译器报告以下问题:

错误:无法移出&mut-pointer insertion.rs:6 的取消引用,让key = data [j];

错误:无法移出&mut-pointer insertion.rs:11 data [i + 1] = data [i];

从内置类型的非通用代码转换为通用代码时,我们是否需要特别小心?错误消息听起来很神秘.

[编辑]

根据弗拉基米尔的建议,我试图提出一个适用于T的版本:Ord使用切片的交换功能

pub fn insertion_sort<T : Ord>(data : &mut [T]){
  let n  = data.len();
  for j in range(1, n){
    // we insert data[j] into the sorted sequence 
    //data[0...j-1]
    let mut i = j -1;
    while data[i] > data[i+1]{
        data.swap(i + 1, i);
        if i == 0{
            break;
        }
        i -= 1;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*eev 9

是的,您需要特别小心,因为在您使用的原始代码片段中int,它是隐式可复制的(实现Copy特征),而在第二部分中,您只使用了带有Ord绑定的泛型参数.默认情况下,Rust中的值被移动而不是复制,这确实会对您可以对值执行的操作带来一些限制.如果您编写<T: Ord+Copy>而不仅仅是<T: Ord>- 您的代码将再次开始编译,您可以观察到这一点.然而,这不是一个合适的通用解决方案,因为很多类型都不是Copy.

首先,您应该阅读正式的Rust指南,其中解释了所有其他所有权借用,核心Rust概念,为了有效地使用Rust,绝对需要理解这些概念.您看到的错误是这些概念的结果.基本上,如果您对某些数据结构有参考,则无法将任何内容移出此结构.Slice是对连续数据块的引用; 因为您没有指定Copy绑定T,Rust无法复制切片中的值,也无法移动这些值,因为禁止从引用后面移动.所以它会发出错误.

这种行为听起来有限制性,有时也是如此.很多在其他语言中很自然地完成的事情(主要是在C语言中)不能直接在Rust中完成.作为回报,Rust提供了巨大的安全保障.但是,有时您需要编写一些本身安全的东西,但这种安全性对于编译器来说并不明显.当您实现基本数据结构和算法(如排序)时,通常会发生这种情况.unsafe当然,最终的工具是块,但在这种情况下你不需要它们.Rust在std::mem模块中提供了几个非常有用的功能,特别是swap()replace().但是,特别是对于切片,有一种swap()直接在切片上调用的方法.它以给定的指数交换元素.如果根据交换操作重新配置插入排序,您将能够编写完全通用的代码,Ord即使它们不可复制也适用于所有类型.我强烈建议您尝试这样做,因为这有助于您了解如何编写低级Rust程序.

另一方面,如果您事先知道只使用原始类型int,那么您可以放心地Copy绑定T并保留代码.或者您可以使用更通用的Clone绑定,但是clone()当您从切片中提取值时,您需要调用方法:

pub fn insertion_sort<T: Ord+Clone>(data: &mut [T]) {
  let n = data.len();
  for j in range(1, n) {
    let key = data[j].clone();
    // we insert data[j] into the sorted sequence 
    //data[0...j-1]
    let mut i = j -1;
    while data[i] > key {
        data[i + 1] = data[i].clone();
        if i == 0 {
            break;
        }
        i -= 1;
    }
    data[i] = key;
  }
}
Run Code Online (Sandbox Code Playgroud)