Rust Mutable Iterator example

Date: 2025-06-08
pub trait Explainable {
    fn do_explain(&self)-> String;
}

pub trait HasAccount {
    fn get_account(&self)-> String;
    fn change_account(&mut self, new_value: &str);
}

pub struct Person {
    name: String,
    account: String
}

impl Explainable for Person {
    fn do_explain(&self)-> String {
        return self.name.clone() + " " + self.account.as_str()
    }
}

impl HasAccount for Person {
    fn change_account(&mut self, new_value: &str) {
        self.account = new_value.to_string();
    }
    
    fn get_account(&self)-> String {
        return self.account.clone();
    }
}

fn change_accounts<'a>(persons: impl Iterator<Item = &'a mut Person>) {
    for p in persons {
        println!("Changing person: {}", p.name);
        let mut new_str= String::with_capacity(100);
        new_str.push_str(&p.account);
        new_str.push_str("_020");
        p.change_account(&new_str);
    }
}

fn change_accounts2<'a>(persons: impl Iterator<Item = &'a mut dyn HasAccount>) {
    for p in persons {
        let mut new_str = String::with_capacity(100);
        new_str.push_str(&p.get_account());
        new_str.push_str("_020");
        p.change_account(&new_str);
    }
}

fn change_accounts3<'a, I>(persons: I)
where
    I: IntoIterator<Item = &'a mut dyn HasAccount>,
{
    for p in persons {
        let mut new_str = String::with_capacity(100);
        new_str.push_str(&p.get_account());
        new_str.push_str("_020");
        p.change_account(&new_str);
    }
}

fn change_accounts4<'a, I>(persons: I)
where
    I: IntoIterator<Item = &'a mut Box<dyn HasAccount>>,
{
    for p in persons {
        let mut new_str = String::with_capacity(100);
        new_str.push_str(&p.get_account());
        new_str.push_str("_020");
        p.change_account(&new_str);
    }
}

// Trait with both traits
trait Entity: Explainable + HasAccount {}
impl<T: Explainable + HasAccount> Entity for T {}


fn explain_all<'a>(items: impl IntoIterator<Item = &'a Box<dyn Entity>>) {
    for item in items {
        println!("{}", item.do_explain());
    }
}

fn change_all<'a>(items: impl IntoIterator<Item = &'a mut Box<dyn Entity>>) {
    for item in items {
        let mut new_str = item.get_account();
        new_str.push_str("_mod");
        item.change_account(&new_str);
    }
}


fn get_boxed_entities1() -> Vec<Box<dyn Entity>> {
    vec![
        Box::new(Person { name: "Piet".into(), account: "abc".into() }),
        Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
    ]
}

// return an iterator
fn get_boxed_entities2() -> impl Iterator<Item = Box<dyn Entity>> {
    return vec![
        Box::new(Person { name: "Piet".into(), account: "abc".into() }),
        Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
    ]
    .into_iter()
    .map(|p| p as Box<dyn Entity>)
}



fn main() {
    println!("Hello, world!");

    let mut persons = vec![
        Person { name: "Jan".into(), account: "2345234".into() },
        Person { name: "Kees".into(), account: "342523".into() }
    ];

    for p in persons.iter() { // <- using .iter() here to allow multiple loops
        let s = p.do_explain();
        println!("{}", s);
    }

    for p in persons.iter_mut() {
        let new_str= p.account.clone() + "_010";
        p.change_account(&new_str);
    }

    change_accounts(persons.iter_mut());
    change_accounts(persons.iter_mut());

    change_accounts2(persons.iter_mut().map(|p| p as &mut dyn HasAccount));

    change_accounts3(persons.iter_mut().map(|p| p as &mut dyn HasAccount));


    for p in persons.iter() {
        let s = p.do_explain();
        println!("{}", s);
    }

    // step 2
    
    let mut boxed: Vec<Box<dyn HasAccount>> = vec![
        Box::new(Person { name: "Piet".into(), account: "abc".into() }),
        Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
    ];

    change_accounts4(boxed.iter_mut()); //boxed.iter_mut().map(|p| p.as_mut())

    // step 3

    let mut list: Vec<Box<dyn Entity>> = vec![
        Box::new(Person { name: "Jan".into(), account: "123".into() }),
        Box::new(Person { name: "Kees".into(), account: "456".into() }),
    ];

    // read
    explain_all(list.iter()); //.map(|x| x.as_ref())

    // write
    let iter = list.iter_mut(); //.map(|x| x.as_mut())
    change_all(iter);

    // read again
    explain_all(list.iter()); //.map(|x| x.as_ref())


    let mut list2 = get_boxed_entities1();
    explain_all(list2.iter()); 
    let iter = list2.iter_mut();
    change_all(iter);
    explain_all(list2.iter());


    let mut list3: Vec<Box<dyn Entity>> = get_boxed_entities2().collect();
    explain_all(list3.iter()); 
    change_all(list3.iter_mut());
    explain_all(list3.iter());

    println!("Finished");
}
trait IntoBoxedA<'a>: IntoIterator<Item = &'a Box<dyn A>> {}
impl<'a, T> IntoBoxedA<'a> for T where T: IntoIterator<Item = &'a Box<dyn A>> {}


// 3 variants with the same behaviour:

fn process1<'a, T: IntoBoxedA<'a>>(items: T) {
    ...
}

fn process1<'a, T>(items: T)
where
    T: IntoBoxedA<'a>
{
    ...
}

fn process1<'a>(items: impl IntoBoxedA<'a>) {
    ...
}



👉 Kies impl Trait voor korte functies, en de T: Trait-vorm als je meerdere trait bounds of het concrete generieke type nodig hebt.

Per trait a concrete alias

trait IntoBoxedA<'a>: IntoIterator<Item = &'a Box<dyn A>> {}
impl<'a, T> IntoBoxedA<'a> for T where T: IntoIterator<Item = &'a Box<dyn A>> {}

trait IntoBoxedB<'a>: IntoIterator<Item = &'a Box<dyn B>> {}
impl<'a, T> IntoBoxedB<'a> for T where T: IntoIterator<Item = &'a Box<dyn B>> {}

trait IntoBoxedC<'a>: IntoIterator<Item = &'a Box<dyn C>> {}
impl<'a, T> IntoBoxedC<'a> for T where T: IntoIterator<Item = &'a Box<dyn C>> {}


fn process1<'a, T: IntoBoxedA<'a>>(items: T) {
    ...
}

fn process2<'a, T: IntoBoxedB<'a>>(items: T) {
    ...
}


macro_rules! define_into_boxed {
    ($name:ident, $trait:path) => {
        pub trait $name<'a>: IntoIterator<Item = &'a Box<dyn $trait>> {}
        impl<'a, T> $name<'a> for T
        where
            T: IntoIterator<Item = &'a Box<dyn $trait>>,
        {}
    };
}

trait A {
    fn do_a(&self);
}

trait B {
    fn do_b(&self);
}

trait C {
    fn do_c(&self);
}

define_into_boxed!(IntoBoxedA, A);
define_into_boxed!(IntoBoxedB, B);
define_into_boxed!(IntoBoxedC, C);

fn process1<'a>(items: impl IntoBoxedA<'a>) {
    for a in items {
        a.do_a();
    }
}

fn process2<'a>(items: impl IntoBoxedB<'a>) {
    for b in items {
        b.do_b();
    }
}


Limetime errors

// error[E0597]: `boxed` does not live long enough
// error[E0759]: `boxed` captured by a closure, but it is dropped before the closure is called


let mut boxed: Vec<Box<dyn HasAccount>> = vec![
    Box::new(Person { name: "Piet".into(), account: "abc".into() }),
    Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
];

edit_accounts(boxed.iter_mut().map(|p| p.as_mut()));

// solution: collect into a Vec, this causes the iterator to be used

let mut boxed: Vec<Box<dyn HasAccount>> = vec![
    Box::new(Person { name: "Piet".into(), account: "abc".into() }),
    Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
];

let mapped: Vec<_> = boxed.iter_mut().map(|p| p.as_mut()).collect();
edit_accounts(mapped);

// link original variable lifetime to iterator:
fn make_iter<'a>(
    boxed: &'a mut [Box<dyn HasAccount>]
) -> impl Iterator<Item = &'a mut dyn HasAccount> {
    boxed.iter_mut().map(|b| b.as_mut())
}


// Full example:

trait HasAccount {
    fn set_account(&mut self, account: &str);
}

struct Person {
    name: String,
    account: String,
}

impl HasAccount for Person {
    fn set_account(&mut self, account: &str) {
        self.account = account.to_string();
    }
}

fn make_iter<'a>(
    boxed: &'a mut [Box<dyn HasAccount>]
) -> impl Iterator<Item = &'a mut dyn HasAccount> {
    boxed.iter_mut().map(|b| b.as_mut())
}

fn edit_accounts<'a, I>(accounts: I)
where
    I: Iterator<Item = &'a mut dyn HasAccount>,
{
    for a in accounts {
        a.set_account("updated");
    }
}

fn main() {
    let mut boxed: Vec<Box<dyn HasAccount>> = vec![
        Box::new(Person { name: "Piet".into(), account: "abc".into() }),
        Box::new(Person { name: "Klaas".into(), account: "xyz".into() }),
    ];

    let iter = make_iter(&mut boxed);
    edit_accounts(iter);
}




95580cookie-checkRust Mutable Iterator example