Rust 类型推断因 const generic 失败

问题描述 投票:0回答:1

我在 Rust 的类型推断中遇到了一个失败,我无法理解 - 正如大多数情况一样,我不知道这是否是编译器错误/不足,或者类型不应该可以推断,我只是不明白为什么。不幸的是,一个最低限度的工作示例需要付出一些努力。

我们的基本目标是实现调度到

num
包的功能,以便对具有自定义类型的向量进行数学计算。

我们从三个通用特征开始:

Producer
AbsSub
Power

use num_traits::{Pow, Signed};

pub trait Producer<B, const LEVEL: bool> {
    /// Produce a single element
    fn produce(&self) -> B;
}

pub trait AbsSub<A, B, const LEVEL: bool> {
    // Vec isn't important here, we just need a generic return type
    fn abs_sub(&self, other: &B) -> Vec<A>
    where
        A: Signed,
        B: Producer<A, LEVEL>;
}

pub trait Power<A, B, C, const LEVEL: bool> {
    fn power(&self, other: &C) -> Vec<A::Output>
    where
        A: Pow<B> + Copy,
        C: Producer<B, LEVEL>;
}

当然,任何类型都可以生成自己,所以我们将继续添加一个全面的实现:

impl<A> Producer<A, false> for A // Note the `false` here
where
    A: Copy,
{
    fn produce(&self) -> A {
        *self
    }
}

现在,我们将定义一个包装类型,

Wrapper
,我们也想要生成它。如果我们在没有
Producer
的情况下定义
const bool
,我们就会不走运:我们的一揽子实现将意味着我们对同一类型实现了
Producer
两次。相反,我们将为
true
实现
Producer
版本的
Wrapper

#[derive(Debug, Clone, Copy)]
struct Wrapper<A>(A);

impl<A> Producer<A, true> for Wrapper<A>
where
    A: Copy,
{
    fn produce(&self) -> A {
        self.0
    }
}

最后,我们将添加

AbsSub
Power
以及
main
的实现:

impl<A, B, const LEVEL: bool> AbsSub<A, B, LEVEL> for Vec<A> {
    fn abs_sub(&self, other: &B) -> Vec<A>
    where
        A: Signed,
        B: Producer<A, LEVEL>,
    {
        self.iter().map(|v| v.abs_sub(&other.produce())).collect()
    }
}

impl<A, B, C, const LEVEL: bool> Power<A, B, C, LEVEL> for Vec<A>
where
    C: Producer<B, LEVEL>,
{
    fn power(&self, other: &C) -> Vec<<A>::Output>
    where
        A: Pow<B> + Copy,
    {
        self.iter().map(|v| v.pow(other.produce())).collect()
    }
}

fn main() {
    let vec = vec![1, 2, 3];

    // `AbsSub` compiles and works
    let wrapped = Wrapper { 0: 2 };
    let result = vec.abs_sub(&2);
    assert_eq!(result, vec![0, 0, 1]);
    let result = vec.abs_sub(&wrapped);
    assert_eq!(result, vec![0, 0, 1]);

    let wrapped = Wrapper { 0: 2u8 };
    let result = vec.power(&2u8); // This compiles
    let result = vec.power(&wrapped); // This doesn't
    let result = Power::<_, _, _, true>::power(&vec, &wrapped); // But this does
}

坦率地说,这对我来说是令人难以置信的。但我不明白为什么推理适用于

wrapped
abs_sub
,但不适用于
wrapped
power
。我收到大量编译器错误:

error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
   = note: cannot satisfy `<i32 as Pow<_>>::Output == _`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: required by a const generic parameter in `Power::power`
  --> src/main.rs:7:26
   |
7  | pub trait Power<A, B, C, const LEVEL: bool> {
   |                          ^^^^^^^^^^^^^^^^^ required by this const generic parameter in `Power::power`
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0283]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
   = note: multiple `impl`s satisfying `i32: Pow<_>` found in the `num_traits` crate:
           - impl Pow<u16> for i32;
           - impl Pow<u32> for i32;
           - impl Pow<u8> for i32;
           - impl Pow<usize> for i32;
note: required by a bound in `Power::power`
  --> src/main.rs:10:12
   |
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
9  |     where
10 |         A: Pow<B> + Copy,
   |            ^^^^^^ required by this bound in `Power::power`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0283]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: multiple `impl`s satisfying `Wrapper<u8>: Producer<_, _>` found
  --> src/main.rs:24:1
   |
24 | / impl<A> Producer<A, false> for A
25 | | where
26 | |     A: Copy,
   | |____________^
...
33 | / impl<A> Producer<A, true> for Wrapper<A>
34 | | where
35 | |     A: Copy,
   | |____________^
note: required by a bound in `Power::power`
  --> src/main.rs:11:12
   |
8  |     fn power(&self, other: &C) -> Vec<A::Output>
   |        ----- required by a bound in this associated function
...
11 |         C: Producer<B, LEVEL>;
   |            ^^^^^^^^^^^^^^^^^^ required by this bound in `Power::power`
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~
error[E0284]: type annotations needed
  --> src/main.rs:75:22
   |
75 |     let result = vec.power(&wrapped);
   |                      ^^^^^
   |
note: required for `Vec<i32>` to implement `Power<i32, _, Wrapper<u8>, _>`
  --> src/main.rs:42:34
   |
42 | impl<A, B, C, const LEVEL: bool> Power<A, B, C, LEVEL> for Vec<A>
   |               -----------------  ^^^^^^^^^^^^^^^^^^^^^     ^^^^^^
   |               |
   |               unsatisfied trait bound introduced here
help: try using a fully qualified path to specify the expected types
   |
75 |     let result = <Vec<i32> as Power<i32, B, Wrapper<u8>, LEVEL>>::power(&vec, &wrapped);
   |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++   ~

如果有人能为这种行为提供任何解释,那就太棒了。我怀疑是否有“修复”,但我真的很想知道这是编译器不足还是真正的推理歧义。

rust type-inference hindley-milner const-generics
1个回答
0
投票

让我们在推导时像编译器一样思考

vec.power(&wrapped);

跳过大部分方法查找,我们知道我们正在尝试匹配

Power
Vec<i32>
Wrapper<u8>
上的特征(这些扣除至少是 在错误输出中可用)。

  • A
    i32
  • C
    Wrapper<u8>

什么是

B
?唯一需要解决的是约束:

  • C: Producer<B, LEVEL>
  • A: Pow<B>

不幸的是,这两个约束都不能根据

B
推导出奇异的
A
C
。两者都是“开集”:一个
C
可以实现多个
Producer
,而一个
A
可以实现多个
Pow

Pow
肯定有多种实现
i32
- 错误输出显示了这一点。所以这绝对没有帮助。

然后

Producer
对于
Wrapper<u8>
有两种实现:一种
LEVEL=false
的一揽子实施和一个通用实施
Wrapper<A>
LEVEL=true
。对你来说不幸的是,面对任何 歧义,编译器放弃并要求你澄清。它不 根据
LEVEL
进行任何探索性推论,看看是否有效。

现在,当您指定

<_, _, _, true>
时,这确实为规则提供了足够的帮助 我们的一揽子实施使得只剩下一个实施,并且 由此可以推导出
B

© www.soinside.com 2019 - 2024. All rights reserved.