首页 > 解决方案 > 将特征方法和关联类型标记为默认的专业化时,预期的输出类型会发生变化

问题描述

我想为Rust中的大多数类型实现模运算:Rem

#![feature(specialization)]

use std::ops::{Add, Rem};

/// Define a modulo operation, in the mathematical sense.
/// This differs from Rem because the result is always non-negative.
pub trait Modulo<T> {
    type Output;

    #[inline]
    fn modulo(self, other: T) -> Self::Output;
}

/// Implement modulo operation for types that implement Rem, Add and Clone.
// Add and Clone are needed to shift the value by U if it is below zero.
impl<U, T> Modulo<T> for U
    where
        T: Clone,
        U: Rem<T>,
        <U as Rem<T>>::Output: Add<T>,
        <<U as Rem<T>>::Output as Add<T>>::Output: Rem<T>
    {
    default type Output = <<<U as Rem<T>>::Output as Add<T>>::Output as Rem<T>>::Output;

    #[inline]
    default fn modulo(self, other: T) -> Self::Output {
        ((self % other.clone()) + other.clone()) % other
    }
}

这在没有 s 的情况下编译得很好default,但是有了default,我得到了

error[E0308]: mismatched types
  --> main.rs:
   |
   |     default fn modulo(self, other: T) -> Self::Output {
   |                                          ------------ expected `<U as Modulo<T>>::Output` because of return type
   |         ((self % other.clone()) + other.clone()) % other
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected Modulo::Output, found std::ops::Rem::Output
   |
   = note: expected type `<U as Modulo<T>>::Output`
              found type `<<<U as std::ops::Rem<T>>::Output as std::ops::Add<T>>::Output as std::ops::Rem<T>>::Output`

我不明白为什么会这样。我需要defaults 因为我想将它专门用于Copy类型。

我每晚使用 Rust 1.29.0。

标签: rusttype-inferencespecialization

解决方案


这是问题的较小再现(一个MCVE):

#![feature(specialization)]

trait Example {
    type Output;
    fn foo(&self) -> Self::Output;
}

impl<T> Example for T {
    default type Output = i32;

    default fn foo(&self) -> Self::Output {
        42
    }
}

fn main() {}

出现问题是因为此实现的专门化可以选择专门化,也可以选择专门化,Outputfoo 不必两者都做

impl<T> Example for T
where
    T: Copy,
{
    type Output = bool;
}

在这种情况下,最初的实现foo将不再有意义——它不再返回类型值Self::Output

当前的专业化实现要求您同时考虑本地和全局,这是您必须在其中阅读错误消息的上下文。这并不理想,但是像这样的问题(以及许多更复杂的事情,我敢肯定)是其中的一部分还不稳定的原因。


推荐阅读