rust-num / num-traits

Numeric traits for generic mathematics in Rust
Apache License 2.0
732 stars 135 forks source link

Why does AsPrimitive require 'static? #117

Open xplorld opened 5 years ago

xplorld commented 5 years ago

I don't see any reason. I want to cast runtime primitives in generic functions and encountered lifetime issues. If we remove the 'static I guess it still works.

cuviper commented 5 years ago

The idea is that this trait is only meant to be used for true primitives, with cheap as conversions, and perhaps for wrappers thereof. The Copy + 'static requirement doesn't fully guarantee this, but it's a decent marker.

It would be a breaking change to remove those requirements, because other code can implicitly make use of those as supertraits of AsPrimitive.

Can you give an example of how this causes lifetime issues for you?

xplorld commented 5 years ago

I am writing a wrapper which takes a u32, as-cast it into i32, doing transformation (i32 -> i32) and as-cast it back to u32. It is like:

fn wrapper(F f) -> G where F: Fn(i32) -> i32, G: Fn(u32) -> u32.

The problem is, I have to write many of them, including u8, u32, u64. I want to write something like

fn wrapper<T, U, F>(F f) -> G where
  T: Copy + AsPrimitive<U>,
  U: Copy + AsPrimitive<T>,
  F: Fn(T) -> T,
  G: Fn(U) -> U

and of course it could not work because of the 'static constraint.

Could you shed some light on that?

cuviper commented 5 years ago

Please include the actual code and the error messages when you report a problem. The snippets you've given are not even valid Rust syntax, regardless of any type or lifetime issues.

I suspect your problem is the lifetime for f, which you'll need to move into the returned closure. Plus that return needs to be an anonymous type, which is perfect for impl Trait.

This works:

pub fn wrapper<T, U, F>(f: F) -> impl Fn(U) -> U
where
    T: AsPrimitive<U>,
    U: AsPrimitive<T>,
    F: Fn(T) -> T,
{
    move |x| f(x.as_()).as_()
}

For more flexibility for the caller, consider taking and returning FnMut instead, so it's possible to have mutable state associated with the F closure.

pub fn wrapper<T, U, F>(mut f: F) -> impl FnMut(U) -> U
where
    T: AsPrimitive<U>,
    U: AsPrimitive<T>,
    F: FnMut(T) -> T,
{
    move |x| f(x.as_()).as_()
}