Profile Picture

Alex Saveau

Alex Saveau

Relentless efficiency

Blog

Generalizing over mutability in Rust

Yes it's cursed

Published Jul 30, 2025 • Last updated Jul 31, 2025 • 1 min read

You’ve seen us generalize over buffer types, now let’s generalize over buffer mutability!

I tried to write something like this in a project I’m working on:

fn process_items<T>(buf: &<mut> [T], mut f: impl FnMut(&<mut> T)) {
    loop {
        ...
        f(&<mut> buf[i]);
    }
}

process_items abstracts over complexity retrieving items from buf and needs to support processing items both mutably and immutably. Unfortunately, this isn’t possible in Rust as far as I’m aware. I first tried solving the problem with a macro, but couldn’t figure out how to generalize over &buf[i] in one macro invocation and &mut buf[i] in another.

Thus, it is with great (dis?)pleasure that I present to you: trait magic! Feast your eyes:

trait Buf<T> {
    type F;

    fn do_(&mut self, f: &mut Self::F, i: usize) -> bool;
}

impl<T, F: FnMut(&T)> Buf<T> for (&[T], PhantomData<F>) {
    type F = F;

    fn do_(&mut self, f: &mut F, i: usize) -> bool {
        let (buf, _) = self;
        if i < buf.len() {
            f(&buf[i]);
            true
        } else {
            false
        }
    }
}

impl<T, F: FnMut(&mut T)> Buf<T> for (&mut [T], PhantomData<F>) {
    type F = F;

    fn do_(&mut self, f: &mut F, i: usize) -> bool {
        let (buf, _) = self;
        if i < buf.len() {
            f(&mut buf[i]);
            true
        } else {
            false
        }
    }
}

fn process_items<T, B: Buf<T>>(mut buf: B, mut f: B::F) {
    let mut i = 0;
    loop {
        if !buf.do_(&mut f, i) {
            break;
        }
        i += 1;
    }
}

fn main() {
    let foo = ["a", "b", "c"];
    process_items((foo.as_slice(), PhantomData), |item| println!("{item}"));
    println!();

    let mut bar = ["a".to_string(), "b".to_string(), "c".to_string()];
    process_items((bar.as_mut_slice(), PhantomData), |item| {
        *item = format!("{item}{item}")
    });
    println!("{bar:?}");
}

Here’s a playground link for your convenience.

The example is contrived, but I hope it gets the trick across. If anybody knows of a simpler solution, I’d be very happy to hear it. :)