我如何接受可以被`..`切片的任意容器?

时间:2018-07-05 13:54:59

标签: rust

我有以下简单设置:

pub trait Distribution {
    type T;
    fn sample(&self) -> Self::T;
}

pub fn foo<D, U>(dist: &D, f: &Fn(&[f64]))
where
    D: Distribution<T = U>,
    U: std::ops::Index<usize>,
{
    let s = dist.sample();
    f(&s[..]);
}

foo接受一个实现Distribution和一个功能的通用容器,但是如果我在this这样的示例中使用它,则该容器:

struct Normal {}

impl Distribution for Normal {
    type T = Vec<f64>;
    fn sample(&self) -> Self::T {
        vec![0.0]
    }
}

fn main() {
    let x = Normal {};
    let f = |_x: &[f64]| {};
    foo(&x, &f);
}

它不起作用,因为f(&s[..]);的类型不是slice:

error[E0308]: mismatched types
  --> src/main.rs:12:10
   |
12 |     f(&s[..]);
   |          ^^ expected usize, found struct `std::ops::RangeFull`
   |
   = note: expected type `usize`
              found type `std::ops::RangeFull`

error[E0308]: mismatched types
  --> src/main.rs:12:7
   |
12 |     f(&s[..]);
   |       ^^^^^^ expected slice, found associated type
   |
   = note: expected type `&[f64]`
              found type `&<U as std::ops::Index<usize>>::Output`

1 个答案:

答案 0 :(得分:6)

您说您将使用usize索引切片:

U: std::ops::Index<usize>,

然后,您使用不是usize 的内容对切片进行索引:

f(&s[..]);

这是RangeFull。正确,编译器不允许您对类型撒谎。

相反,如果您使用正确的类型(如错误消息中所示),它将起作用:

U: std::ops::Index<std::ops::RangeFull>,

然后,该错误与索引的输出类型有关。有关完整说明,请参见Requiring implementation of Mul in generic function

U: std::ops::Index<std::ops::RangeFull, Output = [f64]>,

话虽如此...

  1. 没有理由引入泛型类型U。而是在D的关联类型上添加特征边界。
  2. 您不太可能希望强制动态调度关闭。而是将其作为通用类型:
use std::ops::{Index, RangeFull};

pub fn foo<D, F>(dist: &D, f: F)
where
    D: Distribution,
    D::T: Index<RangeFull, Output = [f64]>,
    F: Fn(&[f64]),
{
    let s = dist.sample();
    f(&s[..]);
}

或等效地:

pub fn foo<D>(dist: &D, f: impl Fn(&[f64]))
where
    D: Distribution,
    D::T: std::ops::Index<std::ops::RangeFull, Output = [f64]>,

但是,对这种通用级别使用RangeFull并不适合我。我会改用AsRef

pub fn foo<D>(dist: &D, f: impl Fn(&[f64]))
where
    D: Distribution,
    D::T: AsRef<[f64]>,
{
    let s = dist.sample();
    f(s.as_ref());
}