Hi,
I am having trouble with recursive types involving generics. This is the minimal example I was able to create:
trait Expr {
fn eval(&self) -> i64;
}
struct Const(i64);
impl Expr for Const {
fn eval(&self) -> i64 { self.0 }
}
enum Either<A,B> { Left(A), Right(B) }
impl<A,B> Expr for Either<A,B> where A: Expr, B: Expr {
fn eval(&self) -> i64 {
match self {
Either::Left(x) => x.eval(),
Either::Right(x) => x.eval()
}
}
}
trait Func {
type Output;
fn call(&self, n: i64) -> Self::Output;
}
impl<F,X> Func for F where F: Fn(i64) -> X {
type Output = X;
fn call(&self, n: i64) -> Self::Output { self(n) }
}
struct Defer<F>(F,i64);
// Try to comment out this impl (along with the 'number' function) and uncomment the specific one below
impl<F> Expr for Defer<F> where F: Func, F::Output: Expr {
fn eval(&self) -> i64 { self.0.call(self.1).eval() }
}
fn number(n: i64) -> impl Expr {
if n <= 0 {
Either::Left(Const(0))
} else {
Either::Right(Defer(number, n-1))
}
}
struct Number;
impl Func for Number {
type Output = Either<Const, Defer<Number>>;
fn call(&self, n: i64) -> Self::Output {
if n <= 0 {
Either::Left(Const(0))
} else {
Either::Right(Defer(Number, n-1))
}
}
}
// Try to uncomment this impl and comment out the generic one above
// impl Expr for Defer<Number> {
// fn eval(&self) -> i64 { self.0.call(self.1).eval() }
// }
#[derive(Clone, Debug)]
struct Foo(Option<Box<Foo>>);
fn main() {
// Try to uncomment this line
// println!("{}", Number.call(7).eval());
println!("{}", number(7).eval());
println!("{:?}", Foo(Some(Box::new(Foo(None)))).clone());
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0275]: overflow evaluating the requirement `Either<Const, Defer<fn(i64) -> impl Expr {number}>>: Expr`
|
note: required for `Defer<fn(i64) -> impl Expr {number}>` to implement `Expr`
--> src/main.rs:31:9
|
31 | impl<F> Expr for Defer<F> where F: Func, F::Output: Expr {
| ^^^^ ^^^^^^^^ ---- unsatisfied trait bound introduced here
= note: 1 redundant requirement hidden
= note: required for `Either<Const, Defer<fn(i64) -> impl Expr {number}>>` to implement `Expr`
For more information about this error, try `rustc --explain E0275`.
error: could not compile `playground` (bin "playground") due to previous error
Yes, there is a cyclic dependency (Defer<Number>: Expr
depends on itself), but I do not understand why that is a problem (simpler recursive types (like struct Foo(Option<Box<Foo>>)
seem to work fine).
Also I tried to use struct Number
instead of fn number
(so I can name the types and experiment with annotations) and it gave me different error message (about Expr
not being implemented for Defer<Number>
or something else along the cycle).
However when I provide the specific impl Expr for Defer<Number>
(and comment out the generic one) the code complies and runs well. This last case convinced me that there is probably nothing fundamentally wrong (like infinite type name or infinitely sized type) and it is all about that cyclic dependency.
I think I have some vague idea on what is going on. If we ask the question "Does Defer<Number>
implement Expr
?" we can answer "No" and there is no contradiction. However if we answer "Yes" that also yields no contradiction. So the compiler perhaps looks for a minimal solution or something like that. But that does not explain why struct Foo(Option<Box<Foo>>)
can implement Clone
, Debug
and such. The reasoning would be the same: If it does not then it does not, there is nothing to force it. Except ...
The impl Clone for Foo
has no (direct) trait bounds. So I think there are two "classes" of "requirements":
#1: Those used to determine which impl
s to choose to satisfy a trait requirement and
#2: Those that are checked after eveything is resolved.
That would explain what happens here. The one unconditional impl Clone for Foo
is chosen and then checked, but no impl Expr for Defer<Number>
is ever chosen because none is "supported" (i.e. there are no "base cases" which it could depend on, not as in "Rust does not support it").
However if this is correct, the followup question would be: How to tell compiler which bounds are #1 and which are #2. For example I would like the compiler to always choose the given impl Expr for Defer<F>
and only then check if F: Func
and F::Output: Expr
. Is it possible? Or am I completely wrong? Is there any way around this?
Tom
(sorry for long post, but I wanted to provide full information of what my problem is)