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:
how compiler know when
(0..)
ends? loop unrolled @ compile time , lambdas evaluated?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
Post a Comment