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

时间:2014-10-20 07:47:45

标签: 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;
  }
}

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

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

编译器报告以下问题:

  

错误:无法移出&mut的解除引用 - 指针插入.rs:6 let key = data [j];

     

错误:无法移出&mut的解除引用 - 指针插入。:11数据[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;
    }
  }
}

2 个答案:

答案 0 :(得分:9)

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

首先,您应该阅读the official Rust guide,其中包括所有权借用,核心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;
  }
}

答案 1 :(得分:1)

实现Copy特征(例如int)的类型在按值传递时被复制。对于所有其他类型,按值传递意味着移动。

添加Copy界限,它将像以前一样工作,但仅限于Copy种类型。

<T : Ord + Copy>

或者,使用Clone绑定并明确克隆元素,而不是移动它们。