iterator - Can a vector be moved and modified without an extra allocation? -
consider following code:
let u: vec<u8> = (64..74).collect(); let v: vec<u8> = u.iter().map(|i| + 1).collect();
u
not moved, therefore v
inevitably newly allocated.
but if following:
let w: vec<u8> = u.into_iter().map(|i| + 1).collect();
u
moved , w
name of transformation. here pseudo-code representing mean:
mark u "moved" = 0..10: u[i] += 1 w = u
there (in opinion) no need new allocation, since map type itself. wouldn't case code:
let t: vec<u8> = (64..74).collect(); let s: string = t.into_iter().map(|i| char).collect();
to summarize question
is there allocation of new vec
when convert vec
iterator , map iterator iterator on elements of same type , collect result vec
?
and if there indeed allocation, why?
i tried --emit=mir
, wasn't able find answer. i'm using rustc 1.20 nightly (if matters).
let's see the source of implementation of 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 *const i8, self.len() isize) *const t } else { begin.offset(self.len() isize) *const t }; let cap = self.buf.cap(); mem::forget(self); intoiter { buf: shared::new(begin), cap: cap, ptr: begin, end: end, } } }
creating intoiter
iterator incurs several allocations, not elements of vector; instead, vector's underlying memory details registered. how the code behind map()
?
fn map<b, f>(self, f: f) -> map<self, f> self: sized, f: fnmut(self::item) -> b, { map{iter: self, f: f} }
no vectors allocated here either. last piece of puzzle collect()
:
fn collect<b: fromiterator<self::item>>(self) -> b self: sized { fromiterator::from_iter(self) }
no answers here; the implementation of from_iter()
vec<t>
?
impl<t> fromiterator<t> vec<t> { #[inline] fn from_iter<i: intoiterator<item = t>>(iter: i) -> vec<t> { <self specextend<t, i::intoiter>>::from_iter(iter.into_iter()) } }
this beginning magic, perhaps related specextend code reveal we're looking for:
impl<t, i> specextend<t, i> vec<t> i: iterator<item=t>, { default fn from_iter(mut iterator: i) -> self { // unroll first iteration, vector going // expanded on iteration in every case when iterable not // empty, loop in extend_desugared() not going see // vector being full in few subsequent loop iterations. // 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> specextend<t, i>>::spec_extend(&mut vector, iterator); vector } default fn spec_extend(&mut self, iter: i) { self.extend_desugared(iter) } }
in code can see vec::new
, vec::with_capacity
methods called allocate fresh space resulting vector.
tl;dr: no, not possible move and modify vector without allocation.
Comments
Post a Comment