【问题标题】:How can I modify elements of a vector in place in Rust?如何在 Rust 中修改向量的元素?
【发布时间】:2018-02-28 20:07:24
【问题描述】:

我正在尝试将一个对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;
        }
    }
}

【问题讨论】:

  • 是的,这是惯用的。也有没有的时候。
  • 谢谢,至少我知道我在正确的轨道上。我也意识到我希望获得与我的问题相关的更多信息,因此我将修改问题
  • let mut v = vec![1, 2, 3]; v[0] = 42;(是的,我知道我似乎很迟钝,但我想看看我是否能在这里找出真正的潜在问题)。
  • 我认为OP的重要问题是“我的想法是向量是不可变的(向量的数据类型和大小永远不会改变)但是向量的内容应该是对整数的可变引用。或者它们应该是整数本身的实际值(而不是引用)?” 我觉得你的第一行想法很奇怪,你的第二个选项(vec 包含整数)是我们大多数人会做什么。

标签: rust


【解决方案1】:

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

引用(&amp;'a T&amp;'a mut T)只能引用由另一个值拥有的值。引用不能拥有他们的引用。

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

在 Rust 中,创建一个可能很大的 Vec 的副本,对其进行操作并返回它是不是更惯用?

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


考虑到这一切,这就是我将如何编写您的代码。请注意,我必须将sieve 中的主循环更改为不直接迭代nums,因为这会导致借用冲突——for 循环需要在nums 上借用,但赋值nums[x] 会还尝试在另一个借用处于活动状态时在 nums 上进行可变借用。我还将&amp;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;
        }
    }
}

【讨论】:

  • 感谢您的周到回答。这消除了我的多个误解,尤其是关于可变向量与具有可变引用的不可变向量的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 2019-07-30
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
  • 2018-05-10
相关资源
最近更新 更多