Why does the unit type (empty tuple) implement the `Extend` trait?
Asked Answered
P

1

14

As per the documentation of unit type (), It implements the Extend trait.

fn extend<T>(&mut self, iter: T) 
where
    T: IntoIterator<Item = ()>, 

Extends a collection with the contents of an iterator.

But I don't really understand the use of it. This allowed me to do the following:

fn main() {
    let mut r = ();
    println!("{:?}", r); // print `()`
    r.extend(vec![(), ()]);
    println!("{:?}", r); // This also print `()`
}

But it does not make any sense to me.

So my question is why does the unit type implement the Extend trait?

Petey answered 4/4 at 8:21 Comment(2)
Seems to me like it's for a similar reason as the FromIterator implementation, allowing you to go from impl Iterator<Item=()> to () as a bottom typeLiterate
it's very useful, my own parser lib have a Accumulator trait implemented for (), and user doesn't have to import anything. It's like the toilet closure .map(|_| ()) then you collect into (), this way you consume the iterator. It's very natural codeDespoliation
D
14

Just a wild guess, but this piece of documentation gives a hint

impl FromIterator<()> for ()

Collapses all unit items from an iterator into one.

This is more useful when combined with higher-level abstractions, like collecting to a Result<(), E> where you only care about errors:

use std::io::*;
let data = vec![1, 2, 3, 4, 5];
let res: Result<()> = data.iter()
    .map(|x| writeln!(stdout(), "{x}"))
    .collect();
assert!(res.is_ok());

That Extend is also used in the standard library to conveniently aggregate function calls as if () were a collection.

Deathly answered 4/4 at 8:46 Comment(1)
I think your assumption is correct. I just found the implementation pull request. This comment and this comment from the author states the same.Petey

© 2022 - 2024 — McMap. All rights reserved.