I've encountered a problem, which I believe boils down to the following:
trait Foo<I> {
type Error: std::error::Error;
fn foo(&mut self, input: I) -> Result<(), Self::Error>;
}
trait Bar<I>: Foo<I> {
fn loop_foo(&mut self, input: I) -> Result<(), Self::Error>;
}
impl<'a, I, T> Bar<&'a mut I> for T
where
for<'b> T: Foo<&'b mut I>,
{
fn loop_foo(&mut self, input: &'a mut I) -> Result<(), Self::Error> {
loop {
self.foo(input)?;
# This some meaningful check, but the details do not matter here
if true {
break Ok(());
}
}
}
}
The compiler rejects the above snippet with the following error message:
error[E0499]: cannot borrow `*input` as mutable more than once at a time
--> src/lib.rs:17:22
|
11 | impl<'a, I, T> Bar<&'a mut I> for T
| -- lifetime `'a` defined here
...
17 | self.foo(input)?;
| ---------^^^^^--
| | |
| | `*input` was mutably borrowed here in the previous iteration of the loop
| returning this value requires that `*input` is borrowed for `'a`
For more information about this error, try `rustc --explain E0499`.
At first, I thought maybe this was some limitation on the borrow checker with mutable state in loop since there's stuff with HRTB going on. I seem to recall reading somewhere that certain things like this are blocked on Polonius.
However, I noticed that if I replace the associated type Error
with the concrete error type, say, std::io::Error
, in the signatures of foo
and loop_foo
, then this compiles fine:
trait Foo2<I> {
fn foo(&mut self, input: I) -> Result<(), std::io::Error>;
}
trait Bar2<I>: Foo2<I> {
fn loop_foo(&mut self, input: I) -> Result<(), std::io::Error>;
}
impl<'a, I, T> Bar2<&'a mut I> for T
where
for<'b> T: Foo2<&'b mut I>,
{
fn loop_foo(&mut self, input: &'a mut I) -> Result<(), std::io::Error> {
loop {
self.foo(input)?;
if true {
break Ok(());
}
}
}
}
That made me think that I could fix the original version by providing some bound either on the associated Error
type, or as a HRTB on the impl for T
. I tried various ways of adding Copy
and/or 'static
to the error type, but I haven't found a solution. In my actual use case, I can get away with just using a concrete error type, but now I'm curious: Why does the first version not work, and is there a trait bound that fixes it?