Usage of `mut` in `impl Trait` parameter

When given the function below with different declarations of for_each, why does one of the forms not cause an error?

 // causes error[E0308] - it says "run" has to be a mutable reference
// fn for_each(f: &mut impl FnMut(T)) {} 

 // Ok - "run" doesnt have to be a mutable variable?
fn for_each(mut f: impl FnMut(T)) {} 


pub fn par_for_each_thing(run: impl FnMut(T)) {
        for_each(run)
}

Am I correctly understanding the difference between the forms below?

f: impl FnMut(T) // <- is this any type that implements that trait?

f: &impl FnMut(T) // <- is this any type that is a "reference" to a type that implements that trait?

f: &mut impl FnMut(T) // <- is this any type that is a "mutable reference" to a type that implements that trait?

mut f: impl FnMut(T) // <- is this any type that implements that trait and the variable binded is mutable?

UPDATE: I apologize for the syntax errors. I think I fixed them all. Thanks for the help!

The code has a number of errors and mismatches, but if I understand the question...

You can only pass mutable references to some type that implements Fn() into this function:

fn for_each(f: &mut impl Fn()) {}

But you can pass any (Sized) type that implements Fn() into this function:

fn for_each(f: impl Fn()) {}

And it's hard to be 100% sure without more context, but this is probably the version you want.

Note that depending on which type of closure you need to support (Fn or FnMut or FnOnce), some combinations of & or &mut won't make much sense. For example, you can't call a &impl FnMut() [1], and you don't need &mut to call an impl Fn(), even if you don't own it.

Yes, your comments are accurate. Note that while &T and &mut T (and T) are different types, the mutability of the binding (left of f:) is just a property of the binding, not some distinct type. For example, instead of

fn foo(mut f: impl FnMut()) {
    f();
}

You could do

fn foo(f: impl FnMut()) {
    let mut f = f;
    f();
}

  1. without some way of knowing it has other capabilities like Clone or something, anyway ↩ī¸Ž

1 Like

The code has a number of errors and mismatches, but if I understand the question...

Sorry about that, I updated the function parameters with FnMut which is what I meant to write.

Thanks for the information.

And it's hard to be 100% sure without more context, but this is probably the version you want.

I want to be able to pass closures to for_each that capture mutable references from the calling scope.

In that case, mut f: impl FnMut() would work.

1 Like

How come there is no error when run in for_each_thing is not mut and for_each is as below?

for_each(mut f: impl FnMut(T))

You're passing it by value. It was immutable while it was called run, but the recipient is free to give the value a mutable binding (mut f).

1 Like

impl FnMut is probably what you want. The &mut in &mut impl FnMut doesn't mean "a closure that captured mutable references" and the lack of &mut in impl FnMut doesn't mean it didn't capture mutable references.

Similarly, you can have a &mut String even though String doesn't contain a &mut Anything and you can have a RefMut as shown below, even though it captures a &mut u32.

struct RefMut(&mut u32);

Side note: In order to call self.for_each(...), for_each will have to take some sort of self parameter.

You're passing ownership of run to for_each, and creating a new binding in the process; that binding can be mutable or not. It's the same idea as I was trying to point out before:

// OK to rebind this thing you own as mutable (doesn't change its type)
fn foo(f: impl FnMut()) {
    let mut f = f;
    f();
}

// Also ok to perform such a binding as part of a function call
fn bar(mut f: impl FnMut()) {}
fn quz(f: impl FnMut()) {
    // Not necessary but you could also:
    // let mut f = f;
    bar(f)
}

Whether or not a parameter binding is mut or not never matters to the caller; just like with let vs let mut, it's more of a lint-like property for the body of the function.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.