I played around a bit with this general train of thought, to get an idea if I like it.
The first result is a unpack_map()
for Iterator
s over n-ary tuples, that will call an n-ary function with the contents of the tuple:
#![feature(unboxed_closures, fn_traits)]
struct UnpackMap<I, F> {
iter: I,
f: F
}
impl<I: Iterator, F: FnMut<I::Item>> Iterator for UnpackMap<I, F> {
type Item = F::Output;
fn next(&mut self) -> Option<F::Output> {
self.iter.next().map(|x| (&mut self.f).call_mut(x))
}
}
trait IteratorExt : Iterator {
fn unpack_map<F>(self, f: F) -> UnpackMap<Self, F>
where Self: Sized, F: FnMut<Self::Item>
{
UnpackMap { iter: self, f: f }
}
}
impl<T> IteratorExt for T where T: Iterator {}
The second is an adaptor as suggested by @abonander:
struct PackedFn<F> {
f: F
}
impl<Args, F: FnOnce<Args>> FnOnce<(Args,)> for PackedFn<F> {
type Output = F::Output;
extern "rust-call" fn call_once(self, a: (Args,)) -> F::Output {
self.f.call_once(a.0)
}
}
impl<Args, F: FnMut<Args>> FnMut<(Args,)> for PackedFn<F> {
extern "rust-call" fn call_mut(&mut self, a: (Args,)) -> F::Output {
(&mut self.f).call_mut(a.0)
}
}
impl<Args, F: Fn<Args>> Fn<(Args,)> for PackedFn<F> {
extern "rust-call" fn call(&self, a: (Args,)) -> F::Output {
(&self.f).call(a.0)
}
}
fn pack<Args, F>(f: F) -> PackedFn<F> where F: FnOnce<Args> {
PackedFn { f: f }
}
Usage examples are as follows:
#[derive(Debug)]
struct V(u32, u32, u32);
fn main() {
let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
for x in a.clone().into_iter().unpack_map(V) {
println!("{:?}", x);
}
for x in a.into_iter().map(pack(V)) {
println!("{:?}", x);
}
}
My general impression is that both are workable (with the current implementation of the Fn
traits), but not worthwhile to add. In particular because what most people really want for higher arity, is something that will accept ((T, U), V)
.