Rust Deref与自动解引用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust Deref与自动解引用相关的知识,希望对你有一定的参考价值。

参考技术A

Deref和DerefMut都是Rust中的trait,用来对指针类型进行转化,得到指针所指向的内容。比如从Box<T>或Rc<T>中得到T,或是从String中得到&str。
从名字中就可以看出来,这Deref是解引用得到一个借用,是共享引用,RerefMut是得到一个可变借用。

定义如下,只要针对某种类型实现了这两个trait,就可以得到指针中的内容。

另外我们经常使用的解引用操作符*其实就是调用了deref函数。例如

我们可以看到,其实 s和 (s.deref())本质上一样,s.deref()和&*s一样

至于自动解引用,就是为了方便程序员,在变量不满足条件的情况下,自动对变量使用解引用,比如rust的库中针对&str实现了很多字符串的操作,然而并没有针对string进行实现,但是我们可以直接使用string进行字符串操作,实际上是编译器自动把&string解引用为了&str。

举个例子

传入的应该是str,但是实际上我们在程序中是对string进行了len的调用,在调用的时候,len接收到的参数是&string,因为编译器发现string没有len方法,因此尝试自动进行解引用,也就是调用deref(),把&string转换为了&str,因此程序可以正常运行。

编译器发现不能够编译通过时,会在三种情况下尝试进行自动解引用,所有的三种情况如下:
1.&T转为&U,其中T: Deref<Target=U>
2.&mut T转为&mut U,其中T: DerefMut<Target=U>
3.&mut T转为&U,其中T: Deref<Target=U>

如何在Rust中修改向量的元素?

我试图将一个Vec(一个切片)的不可变引用传递给一个函数,该函数将使用递增值填充Vec,然后再次迭代它们用零替换其中一些值。

我的想法是向量是不可变的(向量的数据类型和大小永远不会改变),但向量的内容应该是对整数的可变引用。或者它们应该是整数本身的实际值(而不是引用)?

事实证明这是一项艰巨的任务。我读过有关可变性和借用的内容,我觉得我对此有一个很好的理解。我也粗略地了解了引用,解引用,指针等在C中是如何工作的,但我认为我正在努力实现Rust的语法。

我是否以错误的方式思考这个问题?在Rust中,创建一个潜在巨大的Vec的副本,操作它并返回它是更惯用吗?

到目前为止这是我的代码(不编译,很多错误):

#![feature(iterator_step_by)]

pub fn nth(n: usize) {
    let size: usize = (2 as f64 * n as f64 * (n as f64).ln()) as usize;
    // Set an upper bound for seiving.
    let size_sqrt: usize = (size as f64).sqrt().ceil() as usize;
    let nums: Vec<&mut usize> = Vec::with_capacity(size);
    sieve(nums, &size, &size_sqrt);
}

fn sieve(nums: [&mut usize], size: &usize, size_sqrt: &usize) {
    for i in 0..*size {
        nums[i] = i;
    }
    for num in nums {
        if num < 2 {
            continue;
        } else if num > *size_sqrt {
            break;
        }
        for x in (num.pow(2)..size).step_by(*num) {
            nums[x] = 0;
        }
    }
}
答案

我的想法是向量是不可变的(向量的数据类型和大小永远不会改变),但向量的内容应该是对整数的可变引用。或者它们应该是整数本身的实际值(而不是引用)?

引用(&'a T&'a mut T)只能引用另一个值所拥有的值。参考文献不能拥有他们的参照物。

如果你想拥有一个对集合的某些整数进行操作但不一定是连续的函数,那么构建一个对整数的引用向量可能是一个好主意。但是,根据您的代码示例,情况似乎并非如此;向量拥有整数会更简单,更容易。这意味着向量本身需要是可变的。但是,如果要确保函数不会尝试更改向量的大小,则该函数可以接受可变的整数&mut [usize]切片,而不是对向量的可变引用(&mut Vec<usize>)。

在Rust中,创建一个潜在巨大的Vec的副本,操作它并返回它是更惯用吗?

这取决于你之后是否需要再次使用原始的Vec。如果你不这样做,那么在原地改变Vec会更有效率。如果你只需要保留原来的Vec在某些情况下而不是在其他情况下,你可以事先clone() Vec。如果你确实每次都需要原始的Vec,那么返回一个新的Vec可能会更有效率,特别是如果你可以使用collect从迭代器中填充它,因为那样会尝试提前分配正确的大小并且只分配每个在Vec一次的价值。


考虑到这一切,这就是我编写代码的方式。请注意,我必须将sieve中的主循环更改为不直接迭代nums,因为这会导致借用冲突 - for循环需要借用nums,但赋值nums[x]也会尝试在nums上进行可变借用另一个借款是活跃的。我还将&usize参数更改为usize,因为对于小的可复制类型(如原始整数)使用引用没有任何好处(事实上,它可能稍微慢一些)。

#![feature(iterator_step_by)]

pub fn nth(n: usize) {
    let size: usize = (2.0 * n as f64 * (n as f64).ln()) as usize;
    // Set an upper bound for seiving.
    let size_sqrt: usize = (size as f64).sqrt().ceil() as usize;
    let mut nums: Vec<usize> = vec![0; size];
    sieve(&mut nums, size, size_sqrt);
}

fn sieve(nums: &mut [usize], size: usize, size_sqrt: usize) {
    for i in 0..size {
        nums[i] = i;
    }

    for i in 0..size {
        let num = nums[i];
        if num < 2 {
            continue;
        }

        if num > size_sqrt {
            break;
        }

        for x in (num.pow(2)..size).step_by(num) {
            nums[x] = 0;
        }
    }
}

以上是关于Rust Deref与自动解引用的主要内容,如果未能解决你的问题,请参考以下文章

如何在Rust中修改向量的元素?

Rust学习教程 - 引用与借用

将对元组的引用的迭代器解压缩为两个引用集合

Rust语言圣经11 - 引用与借用

Oracle PL/SQL:如何从 VARRAY 的 REF 中进行 DEREF?

Rust学习内存安全探秘:变量的所有权引用与借用