How to define a function with a generic fixed-leng

2020-02-14 02:37发布

问题:

Take an operation on arrays, squaring the length for example.

It's useful to have a generic type (such as f32, f64), but you may also want a generic length, but not a dynamic length.

Here is an example of a squared function that takes 2 arguments.

use std::ops::{Add, AddAssign, Sub, Mul};

const CAN_THIS_BE_GENERIC: usize = 2;

fn squared_length<T>(
    a: &[T; CAN_THIS_BE_GENERIC],
    b: &[T; CAN_THIS_BE_GENERIC]
) -> T
    where T:
        Copy +
        Add +
        AddAssign +
        Add<Output=T> +
        Sub<Output=T> +
        Mul<Output=T>
{
    let mut d: T = a[0] - a[0];  // zero :(
    for (elem_a, elem_b) in a.iter().zip(b.iter()) {
        let elem_dim: T = *elem_a - *elem_b;
        d += elem_dim * elem_dim;
    }
    return d;
}

fn main() {
    println!("Length A! {}", squared_length::<f64>(&[0.5, 3.5], &[10.0, 0.0]));
    println!("Length B! {}", squared_length::<i32>(&[10, -6], &[4, 8]));
    println!("Length C! {}", squared_length::<f32>(&[-3.0, 0.0], &[9.0, 0.0]));
}

Currently the vector length is set at 2.

Is it possible to define generic functions where the size is not dynamic, but generic, the same way types can be generic?

回答1:

No, it's not possible as of Rust 1.25. (May 2018). This (often called "type level integers") is a long requested feature, but it's not yet available in Rust.

There have been several RFCs on this topic. Recently, one finally got accepted: RFC 2000 -- Const Generics. However, it is not implemented yet (tracking issue). I would expect implementation to land end 2018 earliest, but more likely 2019.

There are a few crates simulating type level integers like type-num. It's kind of usable, but I wouldn't call it a full alternative.

Please also note: sometimes it's not really necessary to use type level integer. Your example would work with dynamic sizes, too. Better even: because your function is so small, it's likely to be inlined and then the optimizer can probably figure out all sizes at compile time. So if performance was the only reason to use type level integers, it might not be necessary.



回答2:

First, there are many many APIs for which type-level numbers feel tempting, but you'd increase flexibility by instead using associated types more directly.

That said..

There is a generic-array crate that can almost do this right now, using the aforementioned type-num, but it's gets kinda messy, but should do what you want here. I avoid it myself though.

There is language level progress towards this in both rfcs and the compiler, as well as ongoing discussion around full const-dependent types. I'd therefore kinda expect the generic-array crate to be deprecated in the not too distant future.