Concept Subspace :: sus :: fn :: FnMut
template <class F, class... S>concept FnMut
requires {// Receives and passes along the signature as a pack instead of a single
// argument in order to consistently provide a static_assert() in `Sig` when
// `S` is not a function signature.
{__private::InvokedFnMut<F, typename __private::Sig<S...>::Args>::returns()
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
requires FnOnce<F, S...>;
}
The version of a callable object that is allowed to mutate internal state and may be called multiple times.
A FnMut is typically the best fit for any callable that may be called one
or more times.
Because a FnMut is able to mutate internal state, it may return different
values each time it is called with the same inputs.
A type that satisfies FnMut will return a type that can be converted to
R when called with the arguments Args.... FnMut is satisfied by
being callable as an lvalue (which is done by providing an operator() that
is not &&-qualified). Mutable and const lambdas will satisfy
FnMut.
The second argument of FnMut<F, S> is a function signature with the
format ReturnType(Args...), where Args... are the arguments that will
be passed to the FnMut and ReturnType is what is expected to be
received back. It would appear as a matching concept as:
void function(FnMut<ReturnType(Args...)> auto f) { ... }
Use of FnMut
FnMut should be received by value typically, though it can be received by
reference if mutations should be visible to the caller.
A FnMut should be called by passing it to call_mut
along with any arguments.
This ensures the correct overload is called on the object and
that method pointers are called correctly.
A FnMut may be called any number of times, unlike FnOnce,
and should not be moved when called.
Type erasure
Using a concept like FnMut in a function parameter requires the function
to be a template. Template functions can not be virtual, they must appear
in the header, and they can have a negative impact on binary size. So it can
be desirable to work with a FnMut without templates.
To do so, FnMut supports being type-erased, on the heap or the stack,
into a DynFnMut type.
To receive ownership of a type-erased FnMut, receive a
Box<DynFnMut<R(Args...)>>
instead.
To receive a reference to a type-erased FnMut, receive a
DynFnMut<R(Args...)>&& instead.
See DynConcept for more on type erasure of
concept-satisfying types.
Compatibility
Any callable type that satisfies Fn or FnMut will also satisfy FnOnce.
While a FnOnce should only be called once, this is compatible with the
requirements of FnMut and Fn which can be called only a single time. As
well, FnOnce is allowed to mutate internal state, but it does not have to,
which is compatible with the const nature of Fn.
Examples
A function that receives a FnMut matching type and calls it:
// Accepts any type that can be called once with (Option<i32>) and returns
// i32.
static i32 do_stuff_mut(sus::fn::FnMut<i32(sus::Option<i32>)> auto f) {
return sus::fn::call_mut(f, sus::some(400)) +
sus::fn::call_mut(f, sus::some(100));
}
i32 x = do_stuff_mut([i = 0_i32](sus::Option<i32> o) mutable -> i32 {
i += 1;
return sus::move(o).unwrap_or_default() + i;
});
sus_check(x == 401 + 102);
A FnMut whose first parameter is a class can be matched with a method from
that same class if the remaining parameters match the method's signature:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
i32 map_class_mut(const Class& c,
sus::fn::FnMut<i32(const Class&)> auto f) {
return sus::fn::call_mut(f, c);
}
// Map the class C to its value().
auto c = Class(42);
sus_check(map_class_mut(c, &Class::value) == 42);
Using a method pointer as the parameter for Option::map() will call that
method on the object inside the Option:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
auto o = sus::Option<Class>(Class(42));
sus_check(o.map(&Class::value) == sus::some(42));