How does Rust find out the upper limit of an iterator? -


i'm going through couple of rust examples, , there's particular snippet of code don't understand how works. in particular, this example of higher order functions. focus on snippet of code:

let sum_of_squared_odd_numbers: u32 =     (0..).map(|n| n * n)             // natural numbers squared          .take_while(|&n| n < upper) // below upper limit          .filter(|n| is_odd(*n))     // odd          .fold(0, |sum, i| sum + i); // sum them 

here questions:

  1. how compiler know when (0..) ends? loop unrolled @ compile time , lambdas evaluated?

  2. isn't extremely memory inefficient compared imperative version? example (0..).map(|n| n * n) alone end taking o(n) memory.

how compiler know when (0..) ends?

the compiler doesn't know @ all. range literal, rangefrom. note implements iterator trait. core piece of iterator next:

fn next(&mut self) -> option<self::item> 

that is, given mutable borrow iterator, can return item (some) or signal there no more items (none). possible have iterators go on forever.

in particular example:

  • the range yield every unsigned 32-bit number before stopping .
  • the map stop when underlying iterator stops.
  • the take_while stop when predicate fails or underlying iterator stops.
  • the filter stop when underlying iterator stops.
  • the fold stop when underlying iterator stops.

isn't extremely memory inefficient compared imperative version?

nope! in fact, compiler compile same code imperative version! you'd want check llvm ir or assembly 100% sure, rust's monomorphization capabilities combined llvm's optimizer pretty amazing things.

each iterator adapter pulls enough items previous adapter calculate next value. in example, i'd expect constant memory allocation entire process.

the component of pipeline requires space fold, , needs accumulator value u32. other adapters have no state.

an important thing note calling map, filter, , take_while iterator adaptors doesn't iterator computation @ point in time. return new objects:

// note type // filter<takewhile<map<rangefrom<_>, [closure]>, [closure]>, [closure]> let () =      (0..)     .map(|n| n * n)                 .take_while(|&n| n < 20)     .filter(|n| n % 2 == 0); // @ point, still haven't looked @ single value 

whenever call next on final adaptor, each layer of adaptor stack enough work next value. in original example, fold iterator terminator consumes entire iterator, calling next until there no more values.

bluss points out, don't want try , go past maximum value of range, either panic or loop forever, depending on if built in debug or release mode.


Comments