[英]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种不同的操作:
Sum<T>
boundary to your generic type parameter. Sum<T>
边界添加到泛型类型参数来告诉您的元素是可求和的。 f64
or any float type that you want to limit. 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. 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)
}
}
}
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: 与到目前为止发布的答案相比,它似乎具有以下优点:
num
crate. num
箱。 FromPrimitive
and Zero
. FromPrimitive
和Zero
这样难以猜测的特征。 Or this version which has the following differences to the one above: 或此版本与上述版本有以下差异:
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
. 并添加绑定到
T
的core::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
f64
为ToPrimitive
。 Like this . 这样 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.