Typeof(function)

Do I understand correctly that Rust does not allow to refer to the type of a specific function (basically typeof(fun))?

The reason I'm asking is because I want to do something akin to the following:

trait RefToI32Collection {
    type I32Iter: Iterator<Item = i32>;
    fn iter(self) -> Self::I32Iter;
}

struct MyStruct {
    data: Vec<i32>,
}

fn plus_one(x: &i32) -> i32 {
    return *x + 1;
}

impl<'a> RefToI32Collection for &'a MyStruct {
    type I32Iter = std::iter::Map<std::slice::Iter<'a, i32>, typeof(plus_one)>; 
    // ERROR: Cannot do typeof(plus_one)

    fn iter(self) -> Self::I32Iter {
        return self.data.iter().map(plus_one);
    }
}

I can fix the error message by replacing typeof(plus_one) with fn(&i32) -> i32, but I assume this prevents inlining of the plus_one function and hence comes with a potentially hefty performance penalty.

Correct, there's no plan for typeof.

One day we'll have type alias impl trait (TAIT) for this.

2 Likes

All right. So I guess for the time being my two options are to either take the performance hit or write my own iterator adapter equivalent to .map(plus_one).

No, likely not.

No as in "it won't inline", or no as in "it won't have a significant performance impact"?

The latter. It still can be inlined, probably.

1 Like

Interesting. I guess the point is that when the compiler sees MyStruct::iter(), it can infer a tighter return type than then one specified? But this would no longer work once I have Box<dyn RefToI32Collection>::iter(), right?

At least in simple case it will be - this playground, when using the "show assembly" option, shows that do_iter doesn't use neither plus_one nor MyStruct::iter, so the whole thing was indeed inlined.

2 Likes

It doesn't really have anything to do with the type. It depends on the semantics and complexity of the code, and how smart the optimizer is. Even Box<dyn Fn(…) -> …> can be de-virtualized in theory, although I don't know how much of that is being done.

2 Likes

On nightly, it's possible to use impl with type_alias_impl_trait feature.

impl<'a> RefToI32Collection for &'a MyStruct {
    type I32Iter = std::iter::Map<std::slice::Iter<'a, i32>, impl Fn(&i32) -> i32>;

    fn iter(self) -> Self::I32Iter {
        return self.data.iter().map(plus_one);
    }
}

or impl everything, no need to do that just for the function:

impl<'a> RefToI32Collection for &'a MyStruct {
    type I32Iter = impl Iterator<Item = i32>;

    fn iter(self) -> Self::I32Iter {
        return self.data.iter().map(plus_one);
    }
}

On stable, the best you can do is to define your own iteration structure.

impl<'a> RefToI32Collection for &'a MyStruct {
    type I32Iter = RefToI32CollectionIter<'a>;

    fn iter(self) -> Self::I32Iter {
        RefToI32CollectionIter(self.data.iter())
    }
}

struct RefToI32CollectionIter<'a>(std::slice::Iter<'a, i32>);

impl<'a> Iterator for RefToI32CollectionIter<'a> {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        self.0.next().map(plus_one)
    }
}
1 Like