I am toying around with the unstable try_trait_v2
features, and I found a conflicting implementation error I can't quite wrap my head around. Here's my code; please ignore that the type looks useless
#![feature(try_trait_v2)]
#![feature(try_trait_v2_residual)]
use std::{convert::Infallible, hint, ops::{ControlFlow, FromResidual, Residual, Try}};
pub struct Surely<T>(pub T);
impl<T> FromResidual<Surely<Infallible>> for Surely<T> {
fn from_residual(_: Surely<Infallible>) -> Surely<T> {
unsafe { hint::unreachable_unchecked() }
}
}
impl<T> Try for Surely<T> {
type Output = T;
type Residual = Surely<Infallible>;
fn branch(self) -> ControlFlow<Surely<Infallible>, T> {
ControlFlow::Continue(self.0)
}
fn from_output(output: T) -> Surely<T> {
Surely(output)
}
}
impl<T> Residual<T> for Surely<Infallible> {
type TryType = Surely<T>;
}
impl<T> FromResidual<Surely<Infallible>> for Option<T> {
fn from_residual(_: Surely<Infallible>) -> Option<T> {
unsafe { hint::unreachable_unchecked() }
}
}
The compiler fails with:
error[E0119]: conflicting implementations of trait `FromResidual<Surely<Infallible>>` for type `Option<_>`
--> src/main.rs:26:1
|
26 | impl<T> FromResidual<Surely<Infallible>> for Option<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> FromResidual for Option<T>;
However, there is only one implementation in stdlib of FromResidual<R>
for Option<T>
, with R = Option<Infallible>
.
Why is this an implementation conflict at all? The types Option<Infallible>
and Surely<Infallible>
are clearly distinct (former is populated, latter is not). Am I missing something about how the compiler unifies type terms, is this a compiler bug or is there an undocumented additional implementation of FromResidual
for Option<T>
in stdlib?
jofas
August 29, 2024, 6:58am
2
This is a known issue, unfortunately still open:
opened 06:54AM - 30 Jul 22 UTC
A-associated-items
C-bug
T-types
A-coherence
<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the … bug,
along with any information you feel relevant to replicating the bug.
-->
I tried this code:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=5a6bc60775d41ce5ef3ff9656d489c51
```rust
#![feature(try_trait_v2)]
struct Flip<T>(T);
impl<T> std::ops::FromResidual<Flip<T>> for Option<T>
{
fn from_residual(residual: Flip<T>) -> Self {
Some(residual.0)
}
}
```
I expected to see this happen: Code compiles successfully
Instead, this happened:
```
error[[E0119]](https://doc.rust-lang.org/nightly/error-index.html#E0119): conflicting implementations of trait `std::ops::FromResidual<Flip<_>>` for type `std::option::Option<_>`
--> src/lib.rs:3:1
|
3 | impl<T> std::ops::FromResidual<Flip<T>> for Option<T>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> FromResidual for Option<T>;
```
As you you see, the stdlib has
```rust
impl<T> FromResidual for Option<T> {}
```
Which makes use of a default type arg on `FromResidual` - `<Self as Try>::Residual`. For `Option<T>`, that is `Option<Infallible>`.
So apparently, `FromResidual<Option<Infallible>>` conflicts with `FromResidual<Flip<T>>`.
Playing devil's advocate, we could assume that `FromResidual` is being conservative with its conflicts because `Try::Residual` could change, but it could never change to a type I own.
### Meta
<!--
If you're using the stable version of the compiler, you should also check if the
bug also exists in the beta or nightly versions.
-->
`rustc --version --verbose`:
```
rustc 1.64.0-nightly (3924dac7b 2022-07-29)
binary: rustc
commit-hash: 3924dac7bb29bc8eb348059c901e8f912399c857
commit-date: 2022-07-29
host: aarch64-apple-darwin
release: 1.64.0-nightly
LLVM version: 14.0.6
```
1 Like
Ah, a good old nightly bite in the butt Thanks for the help!
1 Like
Wait, isn't this a fix from 2 weeks ago? I'm on limited connection at the moment, I'll update the compiler and check if it's working now.
1 Like
jofas
August 29, 2024, 7:24am
5
Jup, looks like the PR merged a temporary fix for FromResidual
. Indeed looks like yesterday's nightly compiler can compile your snippet .
1 Like
Hardly; we have something nearly identical in the standard library:
/// An adapter for implementing non-try methods via the `Try` implementation.
///
/// Conceptually the same as `Result<T, !>`, but requiring less work in trait
/// solving and inhabited-ness checking and such, by being an obvious newtype
/// and not having `From` bounds lying around.
///
/// Not currently planned to be exposed publicly, so just `pub(crate)`.
#[repr(transparent)]
pub(crate) struct NeverShortCircuit<T>(pub T);
impl<T> NeverShortCircuit<T> {
/// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`.
///
/// This is useful for implementing infallible functions in terms of the `try_` ones,
/// without accidentally capturing extra generic parameters in a closure.
#[inline]
pub fn wrap_mut_1<A>(mut f: impl FnMut(A) -> T) -> impl FnMut(A) -> NeverShortCircuit<T> {
move |a| NeverShortCircuit(f(a))
}
This file has been truncated. show original
(You'll note that there's no need for unsafe
.)
1 Like
Sorry for the blackout, life happened
Yes, indeed everything works with the latest (nightly) compiler version. Thanks a lot for your help!
1 Like
Ah this is awesome - that was exactly my use case. Not as dumb as idea as I feared then
Just in case you happen to know: why the move
functions in there? I can't figure out what those comments refer to, or what are they trying to avoid.
They're used in iterator adapters in a bunch of places. Having them be separate, rather than inline closures in those places, is done because it avoids needing to monomorphize over as many generic parameters.
Basically, it's a minor compiler-time optimization that I probably wouldn't bother if it wasn't core
.
1 Like
system
Closed
December 12, 2024, 5:19pm
10
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.