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).

if want play code: try online!

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

Popular posts from this blog

Is there a better way to structure post methods in Class Based Views -

performance - Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? -

jquery - Responsive Navbar with Sub Navbar -