Reputation: 19969
I want to implement a modulo operation for most Rem
types in Rust:
#![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
}
}
This compiles fine without the default
s, but with the default
, I get
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`
I don't understand why that would happen. I need the default
s because I want to specialize it for Copy
types.
I'm using Rust 1.29.0-nightly.
Upvotes: 1
Views: 157
Reputation: 430791
Here is a smaller reproduction of the problem (an 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() {}
The problem arises because a specialization of this implementation can choose to specialize either Output
or foo
but it doesn't have to do both:
impl<T> Example for T
where
T: Copy,
{
type Output = bool;
}
In this case, the original implementation of foo
would no longer make sense — it's not returning a value of type Self::Output
anymore.
The current implementation of specialization requires you to think both locally and globally, which is the context you have to read the error message in. It's not ideal, but problems like this (and many more complicated things, I'm sure) are part of the reason that it isn't stable yet.
Upvotes: 1