Stream vs Effect — When to Use Each
The choice is simple: how many values does your computation produce?
Effect<A, E, R> → produces exactly one A (or fails)
Stream<A, E, R> → produces zero or more A values over time (or fails)
Concrete Examples
#![allow(unused)] fn main() { // Effect: get one user fn get_user(id: u64) -> Effect<User, DbError, Db> // Stream: all users, one at a time fn all_users() -> Stream<User, DbError, Db> // Effect: count rows fn count_orders() -> Effect<u64, DbError, Db> // Stream: export all orders for a report fn export_orders() -> Stream<Order, DbError, Db> }
If you fetch 10 million rows into a Vec and return it as an Effect, you'll run out of memory. A Stream loads and processes them incrementally.
Stream Transformations
Stream has the same transformation API as Effect:
#![allow(unused)] fn main() { all_users() .filter(|u| u.is_active()) .map(|u| UserSummary::from(u)) .take(100) }
.map, .filter, .flat_map, .take, .drop, .zip — all work on streams. None of them load the whole stream into memory; they process elements as they arrive.
Collecting a Stream into an Effect
When you do need all the results:
#![allow(unused)] fn main() { let users: Effect<Vec<User>, DbError, Db> = all_users().collect(); }
.collect() consumes the stream and accumulates into a Vec. Use only when the full result fits in memory.
For large results, prefer a fold or a sink:
#![allow(unused)] fn main() { let count: Effect<usize, DbError, Db> = all_users().fold(0, |acc, _| acc + 1); }
Converting Effect to Stream
Wrap an Effect in a single-element stream when you need to compose with streaming operators:
#![allow(unused)] fn main() { use id_effect::Stream; let single_user_stream: Stream<User, DbError, Db> = Stream::from_effect(get_user(1)); // Now compose with other streams let combined = single_user_stream.chain(all_users()); }
The Rule
- Need one result:
Effect - Need to process many results without loading all at once:
Stream - Need to compose multiple streams:
Streamwithchain,zip,merge - Need all results in memory:
Stream+.collect()(with appropriate size caution)