vector - 可以在没有额外分配的情况下移动和修改矢量吗?

标签 vector iterator rust compiler-optimization

考虑以下代码:

let u: Vec<u8> = (64..74).collect();
let v: Vec<u8> = u.iter().map(|i| i + 1).collect();

u 没有被移动,因此 v 不可避免地被重新分配。

但如果我执行以下操作:

let w: Vec<u8> = u.into_iter().map(|i| i + 1).collect();

u 被移动,w 是其转换的名称。这里有一些伪代码代表了我的意思:

mark u as "moved"
for i = 0..10:
    u[i] += 1
w = u

(在我看来)不需要新的分配,因为我们将类型映射到自身。这段代码不是这种情况:

let t: Vec<u8> = (64..74).collect();
let s: String = t.into_iter().map(|i| i as char).collect();

总结我的问题

当我们将Vec转换为迭代器,然后将此迭代器映射到相同类型元素上的迭代器时,是否分配了一个新的Vec 然后将结果收集到 Vec?

如果确实有分配,为什么?

我试过 --emit=mir,但找不到答案。我每晚都在使用 rustc 1.20(如果重要的话)。

If you want to play with code: Try it online!

最佳答案

让我们看看the source into_iter() 的实现对于 Vec<T> :

fn into_iter(mut self) -> IntoIter<T> {
    unsafe {
        let begin = self.as_mut_ptr();
        assume(!begin.is_null());
        let end = if mem::size_of::<T>() == 0 {
            arith_offset(begin as *const i8, self.len() as isize) as *const T
        } else {
            begin.offset(self.len() as isize) as *const T
        };
        let cap = self.buf.cap();
        mem::forget(self);
        IntoIter {
            buf: Shared::new(begin),
            cap: cap,
            ptr: begin,
            end: end,
        }
    }
}

创建 IntoIter迭代器会产生一些额外的分配,但不会针对向量的元素;相反,向量的底层内存细节被注册。 the code怎么样背后map()

fn map<B, F>(self, f: F) -> Map<Self, F> where
    Self: Sized, F: FnMut(Self::Item) -> B,
{
    Map{iter: self, f: f}
}

这里也没有分配额外的向量。最后一 block 拼图是 collect() :

fn collect<B: FromIterator<Self::Item>>(self) -> B where Self: Sized {
    FromIterator::from_iter(self)
}

这里没有答案;怎么样the implementationfrom_iter()对于 Vec<T>

impl<T> FromIterator<T> for Vec<T> {
    #[inline]
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Vec<T> {
        <Self as SpecExtend<T, I::IntoIter>>::from_iter(iter.into_iter())
    }
}

这开始看起来很神奇,但也许是相关的 SpecExtend code将揭示我们正在寻找的内容:

impl<T, I> SpecExtend<T, I> for Vec<T>
    where I: Iterator<Item=T>,
{
    default fn from_iter(mut iterator: I) -> Self {
        // Unroll the first iteration, as the vector is going to be
        // expanded on this iteration in every case when the iterable is not
        // empty, but the loop in extend_desugared() is not going to see the
        // vector being full in the few subsequent loop iterations.
        // So we get better branch prediction.
        let mut vector = match iterator.next() {
            None => return Vec::new(),
            Some(element) => {
                let (lower, _) = iterator.size_hint();
                let mut vector = Vec::with_capacity(lower.saturating_add(1));
                unsafe {
                    ptr::write(vector.get_unchecked_mut(0), element);
                    vector.set_len(1);
                }
                vector
            }
        };
        <Vec<T> as SpecExtend<T, I>>::spec_extend(&mut vector, iterator);
        vector
    }

    default fn spec_extend(&mut self, iter: I) {
        self.extend_desugared(iter)
    }
}

在这段代码中我们终于可以看到 Vec::newVec::with_capacity调用的方法为结果向量分配新空间。

TL;DR:不,在没有额外分配的情况下不可能移动修改向量。

关于vector - 可以在没有额外分配的情况下移动和修改矢量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45753923/

相关文章:

java - 如何打印出 LinkedHashMap 的内容

rust - 如何通过函数中的可变引用重新分配数组

json - 反序列化包含不同类型对象的JSON数组

windows - 路径::read_dir和Windows

c++ - 如果我在指向 vector 空元素的迭代器上调用方法,会发生什么?

c++ - 为什么在英特尔编译器上将 OpenMP 的 firstprivate 与 std::vector 结合使用时会出现未定义的行为?

c++ - 关于C++迭代器 `map`的问题

c++ - 为什么 string::begin() 在立即窗口中输出整个字符串

matlab - 使用matlab获得矢量场和矢量场在闭合曲线上形成的角度?

javascript - 在javascript中获取最接近的笛卡尔轴对齐向量