均值函数的Rust泛型语法

时间:2019-01-13 11:23:46

标签: generics math syntax rust

我正在尝试编写一个函数,该函数采用一片数字并计算均值。

我尝试使用Implementing mean function for generic types中的想法,但遇到错误。

我的代码是:

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)
        }
    }
}

错误是:

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

有没有不用实验特征就可以编写通用均值函数的方法吗?

4 个答案:

答案 0 :(得分:7)

您的类型还需要实现Sum<T>,因此您也需要添加Sum绑定。这还不够,还需要将泛型转换为f64。 num箱子还具有ToPrimitive特质来做到这一点:

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>();
            FromPrimitive::from_usize(numbers.len())
                .and_then(|length: f64| T::to_f64(&sum).and_then(|val| Some(val / length)))
        }
    }
}

Playground

答案 1 :(得分:3)

其他答案可能会帮助您解决一般编写此函数的 real 问题。


您询问的实际错误仅仅是语法错误。您是这样写的:

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

但是几乎可以肯定要写:

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

编译器已经看到您意外包含的:,并认为您正在尝试使用类型归属。类型说明是一种语法,可以在表达式中内联使用类型注释,而不仅仅是在变量声明中使用

您写的与以下内容非常相似:

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

如果您要在每晚的rustc构建中启用类型归因,该错误将改变,因为现在编译器将告诉您sum是一个函数,并且 not 肯定具有类型{ {1}}。

答案 2 :(得分:1)

如何?

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

与到目前为止发布的答案相比,它似乎具有以下优势:

  1. 更简单的泛型类型定义。
  2. 不依赖外部num条板箱。
  3. 不需要FromPrimitiveZero这样难以猜测的特征。
  4. 没有手动的生命周期声明。

此版本与上述版本有以下差异:

  1. 可以使用数组而不是向量。
  2. 不消耗数组(或向量)。
  3. 需要手动的生命周期声明。
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),
    }
}

感谢我的朋友Sven对代码的贡献。

答案 3 :(得分:0)

  • 当编译器无法确定S的类型fn sum<S>(self) -> S时,您需要编写let foo: Bar = baz.sum();let foo = baz.sum::<Bar>();

  • 如果您确定T始终是某种数字原语,则应使用sum()let sum: T = numbers.iter().cloned().sum();收集一个拥有的类型,然后添加{{1 }}绑定到core::iter::Sum。否则,您可能需要使用参考。

  • 返回T可以使函数更通用,但是如果您确实要返回Option<T>,则应使用以下命令将Option<f64>强制转换为T f64特质。像this

相关问题