Can one alias higher rank trait bound in rust? - rust

My program has a bunch of functions that operate on generic integer. They are usually of the following form:
use num::{FromPrimitive, Integer, ToPrimitive};
use std::cmp::Ord;
use std::ops::{Add, Mul};
fn function<'a, I>(n: &'a I) -> I
where
I: Integer + Clone + FromPrimitive + ToPrimitive,
for<'b> &'b I: Mul<Output = I> + Add<Output = I> + Ord,
{
}
I want to alias the generic type requirements:
I: Integer + Clone + FromPrimitive + ToPrimitive,
for<'b> &'b I: Mul<Output = I> + Add<Output = I> + Ord,
so that I won't need to rewrite them every time. Initially, I thought macros would help but it looks like they don't work like in C so I looked for another way.
I found a way to do it for the first requirement. One has to apply the default implementation on the defined trait over any type T.
trait GInteger: Integer + Clone + FromPrimitive + ToPrimitive {}
impl<T: Integer + Clone + FromPrimitive + ToPrimitive> GInteger for T {}
Then I can simply write:
I: GInteger
instead of
I: Integer + Clone + FromPrimitive + ToPrimitive,
How can I alias the second requirement? Is it possible?
for<'b> &'b I: Mul<Output = I> + Add<Output = I> + Ord,

No it's not possible to use a new trait for this.
While it's possible to include the second requirement into the trait definition...
trait GInteger: Integer + Clone + FromPrimitive + ToPrimitive
where
for<'b> &'b Self: Mul<Output = Self> + Add<Output = Self> + Ord,
{
}
rustc will not elaborate the where clause for you, so in the declaration of function() you still need to write the where for<'b> &'b I: ... bound. It is a known bug.
fn function<I: GInteger>(n: &I) -> I
where
for<'b> &'b I: Mul<Output = I> + Add<Output = I> + Ord, // meh
{
n * n
}
If you're using nightly Rust, you could use trait alias (RFC 1733) instead, which exactly solves this problem.
#![feature(trait_alias)]
use num::{FromPrimitive, Integer, ToPrimitive};
use std::cmp::Ord;
use std::ops::{Add, Mul};
// Define a trait alias
trait GInteger = Integer + Clone + FromPrimitive + ToPrimitive
where
for<'b> &'b Self: Mul<Output = Self> + Add<Output = Self> + Ord;
// Just use it
fn function<I: GInteger>(n: &I) -> I {
n * n
}

Related

Why does `Fn() -> T` constrain `T` but `Fn(T) -> T` does not

The following code compiles fine:
struct StructA<F>(F);
impl<F, T> StructA<F> where F: Fn() -> T {}
Although T doesn't show up in StructA's type parameters, it is still constrained due to the where clause. This trick is used, for example, in std::iter::Map so Map<I, F> only needs two type parameters while the impl<B, I, F> Iterator for Map<I, F> takes three.
However the following code does not compile:
struct StructB<F>(F);
impl<F, T> StructB<F> where F: Fn(T) -> T {}
error[E0207]: the type parameter `B` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:5:9
|
5 | impl<F, T> StructB<F> where F: Fn(T) -> T {}
| ^ unconstrained type parameter
For more information about this error, try `rustc --explain E0207`.
error: could not compile `playground` due to previous error
Playground Link
This is unintuitive, why would using T in more places make it less constrained? Is this intended or is it a limitation in Rust?
Note this also happens with regular traits, i.e. the desugared version of Fn:
trait FnTrait<Args> {
type Output;
}
// Works
struct StructA<F>(F);
impl<F, T> StructA<F> where F: FnTrait<(), Output = T> {}
// Fails
struct StructB<F>(F);
impl<F, T> StructB<F> where F: FnTrait<(T,), Output = T> {}
Playground Link
Consider if we implement Fn manually (of course this requires nightly)...
#![feature(fn_traits, unboxed_closures)]
struct MyFunction;
impl<T> FnOnce<(T,)> for MyFunction {
type Output = T;
extern "rust-call" fn call_once(self, (v,): (T,)) -> T { v }
}
Now imagine your struct:
struct StructA<F>(F);
impl<F: FnOnce(T) -> T, T> StructA<F>{
fn foo(self) -> T { (self.0)() }
}
let s: StructA<MyFunction> = ...;
s.foo(); // What is `T`?
While the reference says:
Generic parameters constrain an implementation if the parameter appears at least once in one of:
...
As an associated type in the bounds of a type that contains another parameter that constrains the implementation
This is inaccurate. Citing the RFC:
Type parameters are legal if they are "constrained" according to the following inference rules:
...
If <T0 as Trait<T1...Tn>>::U == V appears in the impl predicates, and T0...Tn are constrained and T0 as Trait<T1...Tn> is not the impl trait reference then V is constrained.
That is, all type parameters should that appear in the trait should be constrained, not just one of them.
I've opened an issue in the reference repo.
Related: https://github.com/rust-lang/rust/issues/25041.
You always need to be able to deduce the generic type parameters of an impl from the self type of the impl (and possibly the trait being implemented, if it's a trait impl). In the first case, F: Fn() -> T, it is possible to derive T from the self type StructA<F>. However, with the trait bound F: Fn(T) -> T, this is not possible.
The difference between the two cases results from the fact that the return type of the closure trait Fn is an associated type, while the argument types are generic parameters. In other words, you can only implement Fn() -> T for any type F once, and that implementation will have a fixed return type T. The trait Fn(T) -> T, on the other hand, could be implemented for multiple types T for the same F, so given F you can't deduce what T is in general.
In practice it is of course very uncommon that multiple Fn traits are implemented for the same type, and when only using closures it's even impossible. However, since it's possible, the compiler needs to account for it.

Writing generic implementation of sum on an iterator in Rust

I have been learning Rust, coming from a Swift, C and C++ background. I feel like I have a basic understanding of ownership, borrowing and traits. To exercise a bit, I decided to implement a sum function on a generic slice [T] where T has a default value and can be added to itself.
This is how far I got:
trait Summable {
type Result;
fn sum(&self) -> Self::Result;
}
impl<T> Summable for [T]
where
T: Add<Output = T> + Default,
{
type Result = T;
fn sum(&self) -> T {
let x = T::default();
self.iter().fold(x, |a, b| a + b)
}
}
Compiler complains with expected type parameter T, found &T for a + b.
I understand why the error happens, but not exactly how to fix it. Yes, the type of x is T. It cannot be &T because, if nothing else, if the slice is empty, that's the value that is returned and I cannot return a reference to something created inside the function. Plus, the default function returns a new value that the code inside the function owns. Makes sense. And yes, b should be a shared reference to the values in the slice since I don't want to consume them (not T) and I don't want to mutate them (not &mut T).
But that means I need to add T to &T, and return a T because I am returning a new value (the sum) which will be owned by the caller. How?
PS: Yes, I know this function exists already. This is a learning exercise.
The std::ops::Add trait has an optional Rhs type parameter that defaults to Self:
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
Because you've omitted the Rhs type parameter from the T: Add<Output = T> bound, it defaults to T: hence to your a you can add a T, but not an &T.
Either specify that T: for<'a> Add<&'a T, Output = T>; or else somehow obtain an owned T from b, e.g. via T: Copy or T: Clone.

How do I make a closure that avoids a redundant clone of captured variables?

I'm trying to implement the classic make_adder function which takes an addable thing and returns a closure which takes another addable thing and returns the sum. Here is my attempt:
use std::ops::Add;
fn make_adder<T: Add + Clone>(x: T) -> impl Fn(T) -> T::Output {
move |y| x.clone() + y
}
Because I don't want to restrict T to be Copy, I'm calling clone() inside the closure. I think this also means there will always be one redundant x captured by the closure as the "prototype". Can I somehow do this better?
Realistically, you cannot avoid this. You never know if the closure will be called another time; you will need to keep the value in case it is. I wouldn't worry about performing the clone until profiling has identified that this is a bottleneck.
In certain cases, you might be able to change your closure type to FnOnce, which enforces that it can only be called exactly once:
fn make_adder<T>(x: T) -> impl FnOnce(T) -> T::Output
where
T: Add,
{
move |y| x + y
}
In other cases, you might be able to add some indirection to the problem. For example, instead of passing a T, pass in a closure that generates a T (presumably not by cloning its own captured variable...). This T can always be consumed directly:
fn make_adder<T>(x: impl Fn() -> T) -> impl Fn(T) -> T::Output
where
T: Add,
{
move |y| x() + y
}
Perhaps you can use a reference, if you’re using types that support addition on references (probably all the useful ones do, including the built-in numeric types).
fn make_adder<T, U>(x: T) -> impl Fn(T) -> U
where
for<'a> &'a T: Add<T, Output = U>,
{
move |y| &x + y
}
or
fn make_adder<'a, T>(x: &'a T) -> impl Fn(T) -> <&'a T as Add<T>>::Output
where
&'a T: Add<T>,
{
move |y| x + y
}

How do I create a vector of functions with generic type parameters?

I have a few functions with the same generic type signature:
pub fn selection_sort<T: Ord + Debug>(v: &mut [T]) {}
pub fn insertion_sort<T: Ord + Debug>(v: &mut [T]) {}
I want to put them into a Vec or array so that I can iterate over them and run the same tests on them, as follows:
let mut sorters: Vec<fn<T: Ord + Debug>(v: &mut [T])> = Vec::new();
However I get this compiler error:
error: expected `(`, found `<`
--> src/main.rs:8:28
|
8 | let mut sorters: Vec<fn<T: Ord + Debug>(v: &mut [T])> = Vec::new();
| ----------- ^
| |
| while parsing the type for `mut sorters`
How can I put these functions into a vector? Is there a better approach to re-use tests against functions that satisfy this signature?
I'm using Rust 1.24.0.
You cannot have a function pointer with a generic type. You will need to pick a specific type for T:
use std::fmt::Debug;
pub fn selection_sort<T: Ord + Debug>(v: &mut [T]) {}
pub fn insertion_sort<T: Ord + Debug>(v: &mut [T]) {}
fn main() {
let sorters_u8: &[fn(&mut [u8])] = &[selection_sort, insertion_sort];
let sorters_bool: &[fn(&mut [bool])] = &[selection_sort, insertion_sort];
}
The intuition here is that a function pointer has to point to something. Until a specific type has been provided to the function, there isn't any actual code generated — the compiler cannot create an infinite number of functions just in case one of them is chosen later.
See also:
Vector of Generic Structs in Rust
Function pointers in Rust using constrained generics

How to apply trait inside a closure which is used as a function result

I can't implement this LISP construction
(defun foo (n)
(lambda (i) (incf n i)))
in Rust.
I have tried this:
use std::ops::Add;
fn f<T: Add>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
fn main() {
let adder = f(2);
assert_eq!(4, adder(2));
}
But it causes an error:
error: mismatched types:
expected `T`,
found `<T as core::ops::Add>::Output`
(expected type parameter,
found associated type) [E0308]
Box::new(move |i: T| n + i)
^~~~~
Seems like the trait Add defined for the outer function was not transferred into the inner closure.
Is it possible to implement such a construction?
It is possible to implement this function with a concrete type instead of a generic:
fn f(n: i32) -> Box<Fn(i32) -> i32> {
Box::new(move |i| n + i)
}
There are several problems with the generic version.
First, the error you have provided occurs because just T: Add is not enough to specify output type: you need to put a constraint onto the associated type <T as Add>::Output as well (see Add docs):
fn f<T: Add<Output=T>>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
Alternatively, you can make the closure return the output type of <T as Add>:
fn f<T: Add>(n: T) -> Box<Fn(T) -> T::Output> {
Box::new(move |i: T| n + i)
}
However, now you'll get the following error:
<anon>:4:10: 4:37 error: the parameter type `T` may not live long enough [E0310]
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:4:10: 4:37 help: see the detailed explanation for E0310
<anon>:4:10: 4:37 help: consider adding an explicit lifetime bound `T: 'static`...
<anon>:4:10: 4:37 note: ...so that the type `[closure#<anon>:4:19: 4:36 n:T]` will meet its required lifetime bounds
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~
The problem here is that T may contain references inside it (afterwards, it is a generic type - it can contain anything); however, Box<Fn(T) -> T> implicitly means that anything inside this trait object must be 'static, i.e. the compiler automatically adds 'static constraint: Box<Fn(T) -> T + 'static>. However, your closure captures T which can contain any references, not only 'static.
The most general way to fix it would be to add an explicit lifetime constraint of T and Box<Fn(T) -> T>:
fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
Alternatively, you can specify that T is 'static, though this unnecessarily limits the genericity of your code:
fn f<T: Add<Output=T> + 'static>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
However, this still does not compile:
<anon>:4:31: 4:32 error: cannot move out of captured outer variable in an `Fn` closure
<anon>:4 Box::new(move |i: T| n + i)
^
This error happens because addition in Rust (i.e. Add trait) works by value - it consumes both arguments. For Copy types, like numbers, it is fine - they are always copied. However, the compiler can't assume that generic type parameters also designate Copy types because there is no respective bound, therefore it assumes that values of type T can only be moved around. However, you're specifying that the returned closure is Fn, so it takes its environment by reference. You can't move out of a reference, and this is what this error is about.
There are several ways to fix this error, the simplest one being to add Copy bound:
fn f<'a, T: Add<Output=T> + Copy + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
And now it compiles.
One possible alternative would be to return FnOnce closure which takes its environment by value:
fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<FnOnce(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
There are two problems with it, however. First, as its name implies, FnOnce can only be called once, because upon its first invocation its environment is consumed, and there is nothing to call it on the next time. This may be overly limiting. Second, unfortunately, Rust cannot call Box<FnOnce()> closures at all. This is an implementation problem and it should be solved in the future; for now there is an unstable FnBox trait to work around this.
Even another alternative would be to use references instead of values:
fn f<'a, T: 'a>(n: T) -> Box<Fn(T) -> T + 'a> where for<'b> &'b T: Add<T, Output=T> {
Box::new(move |i: T| &n + i)
}
Now we specify that instead of T, &'b T for any lifetime 'b must be summable with T. Here we use the fact that Add trait is overloaded for references to primitive types as well. This is probably the most generic version of this function.
If you are OK with moving away from function calling, you could wrap the x in your own type:
use std::ops::Add;
struct Adder<X> {
x: X
}
impl<X: Copy> Adder<X> {
fn add<Y: Add<X>>(&self, y: Y) -> <Y as Add<X>>::Output {
y + self.x
}
}
fn main() {
let x = Adder { x: 1usize };
x.add(2); // as opposed to x(2)
}
This means you can get rid of Box and don't need to allocate anything. Implementing Fn(..) is not possible in stable Rust, and the unstable version may break in future Rust versions. Look at std::ops::Fn for further information.

Resources