简体   繁体   English

均值函数的Rust泛型语法

[英]Rust generics syntax for mean function

I'm trying to write a function which takes a slice of numbers and calculates the mean. 我正在尝试编写一个函数,该函数采用一片数字并计算均值。

I tried using the ideas from Implementing mean function for generic types but get an error. 我尝试使用实现泛型类型的均值函数中的想法,但出现错误。

My code is: 我的代码是:

extern crate num;

use num::{FromPrimitive, Zero};
use std::ops::{Add, Div};

fn main() {
    let mut numbers = [10, -21, 15, 20, 18, 14, 18];
    let err = "Slice is empty.";

    println!("Mean is {:.3}", mean(&numbers).expect(err));
}

fn mean<T>(numbers: &[T]) -> Option<f64>
where
    T: Copy + Zero + Add<T, Output = T> + Div<T, Output = T> + FromPrimitive,
{
    match numbers.len() {
        0 => None,
        _ => {
            let sum = numbers.iter().sum: ();
            let length = FromPrimitive::from_usize(numbers.len()).unwrap();
            Some(sum / length)
        }
    }
}

The error is: 错误是:

error[E0658]: type ascription is experimental (see issue #23416)
  --> src/main.rs:20:23
   |
20 |             let sum = numbers.iter().sum: ();
   |                       ^^^^^^^^^^^^^^^^^^^^^^

Is there any way of writing a generic mean function without using experimental features? 有没有编写任何不使用实验功能的通用均值函数的方法?

You are doing 2 different operations in your generic function: 您在通用函数中执行2种不同的操作:

  • Summing the all values in slice : You need to tell that your elements are summable by adding Sum<T> boundary to your generic type parameter. 对slice中的所有值求和:您需要通过将Sum<T>边界添加到泛型类型参数来告诉您的元素是可求和的。
  • Division operation with 2 elements : Your generic type needs to be converted to f64 or any float type that you want to limit. 用2个元素进行除法运算:您的泛型类型需要转换为f64或要限制的任何浮点类型。 Since you are using num crate i added ToPrimitive as boundary which tells that your generic type can be converted to a primitive type. 由于您使用的是num crate,因此我添加了ToPrimitive作为边界,这告诉您可以将泛型类型转换为原始类型。

Here is the implementation : 这是实现:

fn mean<'a, T: 'a>(numbers: &'a [T]) -> Option<f64>
where
    T: ToPrimitive + Sum<&'a T>,
{
    match numbers.len() {
        0 => None,
        _ => {
            let sum = numbers.iter().sum::<T>();
            let length = f64::from_usize(numbers.len())?;

            T::to_f64(&sum).map(|sum| sum / length)
        }
    }
}

Playground 操场

The other answers will likely help you with your real problem of writing this function generically. 其他答案可能会帮助您解决通用编写此函数的实际问题。


The actual error you've asked about though is just a syntax mistake. 您所询问的实际错误只是语法错误。 You wrote this: 你这样写:

let sum = numbers.iter().sum: ();

But almost certainly intended to write: 但是几乎可以肯定要写:

let sum = numbers.iter().sum();

The compiler has seen the : that you have accidentally included, and thinks that you are trying to use type ascription. 编译器已经看到了:你不小心包括在内,并且认为你要使用类型归属。 Type ascription is syntax to use type annotations inline within an expression, instead of just in variable declarations. 类型说明是在表达式中内联而不是仅在变量声明中使用类型注释的语法。

What you wrote is very similar to: 您写的内容非常类似于:

let sum: () = numbers.iter().sum;

If you were to enable type ascription in a nightly rustc build, the error would change because now the compiler will tell you that sum is a function and definitely does not have type () . 如果你要启用夜间rustc构建类型归属,误差会改变因为现在的编译器会告诉你, sum是一个函数,绝对没有类型()

How about this: 这个怎么样:

use std::iter::Sum;

fn main() {
    let err = "Slice is empty.";

    // Test vector of integers
    let numbers = vec![10i32, -21, 15, 20, 18, 14, 18];
    println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));

    // Test vector of floating point numbers
    let numbers = vec![10f64, -21f64, 15f64, 20f64, 18f64, 14f64, 18f64];
    println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));

    // Test empty vector
    let numbers: Vec<i32> = Vec::new();    
    println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));
}

fn mean<T, I: Iterator<Item = T>>(iter: I) -> Option<f64>
where
    T: Into<f64> + Sum<T>,
{
    let mut len = 0;
    let sum = iter
        .map(|t| {
            len += 1;
            t
        })
        .sum::<T>();

    match len {
        0 => None,
        _ => Some(sum.into() / len as f64)
    }
}

Same code in the Rust Playground Rust Playground中的相同代码

It seems to have the following advantages over the answers posted so far: 与到目前为止发布的答案相比,它似乎具有以下优点:

  1. Much simpler generic type definition. 通用类型定义要简单得多。
  2. No reliance on external num crate. 不依赖外部num箱。
  3. No need for difficult-to-guess traits like FromPrimitive and Zero . 不需要像FromPrimitiveZero这样难以猜测的特征。
  4. No manual lifetimes declarations. 没有手动的生命周期声明。

Or this version which has the following differences to the one above: 或此版本与上述版本有以下差异:

  1. Can take arrays rather than vectors. 可以采用数组而不是向量。
  2. Does not consume the array (or vector). 不消耗数组(或向量)。
  3. Needs manual lifetime declarations. 需要手动生命周期声明。
use std::iter::Sum;

fn main() {
    let err = "Slice is empty.";

    // Test aray of integers
    let numbers = [10, -21, 15, 20, 18, 14, 18];
    println!("Mean is {:.3}", mean(numbers.iter()).expect(err));

    // Test array of floating point numbers
    let numbers = [10f64, -21f64, 15f64, 20f64, 18f64, 14f64, 18f64];
    println!("Mean is {:.3}", mean(numbers.iter()).expect(err));

    // Test empty array
    let numbers: [i32; 0] = [];
    match mean(numbers.iter()) {
        Some(mean_) => println!("Mean is {:.3}", mean_),
        None => println!("Empty array"),
    }
}

fn mean<'a, T, I>(iter: I) -> Option<f64>
where
    T: Into<f64> + Sum<&'a T> + 'a,
    I: Iterator<Item = &'a T>,
{
    let mut len = 0;
    let sum = iter
        .map(|t| {
            len += 1;
            t
        })
        .sum::<T>();

    match len {
        0 => None,
        _ => Some(sum.into() / len as f64),
    }
}

Thanks to my friend Sven for code contribution. 感谢我的朋友Sven对代码的贡献。

  • When the compiler can't figure out the type S of fn sum<S>(self) -> S , you either need to write let foo: Bar = baz.sum(); 当编译器无法找出fn sum<S>(self) -> S的类型S时,您需要编写let foo: Bar = baz.sum(); or let foo = baz.sum::<Bar>(); 或者let foo = baz.sum::<Bar>();

  • If you are sure that T will always be some type of number primitive, you ought to collect a owned type from sum() with let sum: T = numbers.iter().cloned().sum(); 如果确定T总是数字原语的某种类型,则应使用let sum: T = numbers.iter().cloned().sum();sum()收集一个拥有的类型let sum: T = numbers.iter().cloned().sum(); and add the core::iter::Sum bound to T . 并添加绑定到Tcore::iter::Sum Otherwise, you may want to work with references. 否则,您可能需要使用参考。

  • You can make your function a little more generic be returning Option<T> but if you really want to return Option<f64> , you should cast T to f64 using the ToPrimitive trait. 您可以使函数返回Option<T>更加通用,但是如果您确实想返回Option<f64> ,则应使用ToPrimitive特征将T f64ToPrimitive Like this . 这样

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM