You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
with a new design for the currently-unstable [`Try` trait](https://doc.rust-lang.org/nightly/std/ops/trait.Try.html)
11
11
and corresponding desugaring for the `?` operator.
12
12
13
-
The new design supports all the currently-stable conversions (including the accidental ones),
14
-
while addressing the discovered shortcomings of the currently-implemented solution,
15
-
as well as enabling new scenarios.
13
+
The new design includes support for all *intentional* interconversions.
14
+
It proposes removing the *accidental* interconversions, as a crater run
15
+
demonstrated that would be feasible, however includes an alternative system
16
+
that can support them as a low-support-cost edition mechanism if needed.
16
17
17
18
*This is [forward-looking](#future-possibilities) to be compatible with other features,
18
19
like [`try {}`](https://doc.rust-lang.org/nightly/unstable-book/language-features/try-blocks.html) blocks
@@ -27,7 +28,7 @@ The motivations from the previous RFC still apply (supporting more types, and re
27
28
However, new information has come in since the previous RFC, making people wish for a different approach.
28
29
29
30
- Using the "error" terminology is a poor fit for other potential implementations of the trait.
30
-
- The ecosystem has started to see error types which are `From`-convertible from *any* type implementing `Debug`, which makes the previous RFC's method for controlling interconversions ineffective.
31
+
- The previous RFC's mechanism for controlling interconversions proved ineffective, with inference meaning that people did it unintentionally.
31
32
- It's no longer clear that `From` should be part of the `?` desugaring for _all_ types. It's both more flexible -- making inference difficult -- and more restrictive -- especially without specialization -- than is always desired.
32
33
- An [experience report](https://github.com/rust-lang/rust/issues/42327#issuecomment-366840247) in the tracking issue mentioned that it's annoying to need to make a residual type in common cases.
The provided implementation of `try_fold` is already just using `?` and `try{}`, so doesn't change. The only difference is the name of the associated type in the bound:
@@ -855,14 +828,6 @@ In those cases where a separate type *is* needed, it's still easier to make a re
855
828
856
829
This RFC uses `!` to be concise. It would work fine with `convert::Infallible` instead if `!` has not yet stabilized, though a few more match arms would be needed in the implementations. (For example, `Option::from_residual` would need `Some(c) => match c {}`.)
857
830
858
-
## Moving away from the `Option`→`Result` interconversion
859
-
860
-
We could consider doing an edition switch to make this no longer allowed.
861
-
862
-
For example, we could have a different, never-stable `Try`-like trait used in old editions for the `?` desugaring. It could then have a blanket impl, plus the extra interconversion one.
863
-
864
-
It's unclear that that's worth the effort, however, so this RFC is currently written to continue to support it going forward. Notably, removing it isn't enough to solve the annotation requirements, so the opportunity cost feels low.
865
-
866
831
## Why `FromResidual` is the supertrait
867
832
868
833
It's nicer for `try_fold` implementations to just mention the simpler `Try` name. It being the subtrait means that code needing only the basic scenario can just bound on `Try` and know that both `from_output` and `from_residual` are available.
@@ -890,6 +855,85 @@ impl<R: std::fmt::Debug> FromResidual<R> for LogAndIgnoreErrors {
890
855
891
856
And, ignoring the coherence implications, a major difference between the two sides is that the target type is typically typed out visibly (in a return type) whereas the source type (going into the `?`) is often the result of some called function. So it's preferable for any behaviour extensions to be on the type that can more easily be seen in the code.
892
857
858
+
## Can we just remove the accidental interconversions?
859
+
860
+
This depends on how we choose to read the rules around breaking changes.
861
+
862
+
A [crater run on a prototype implementation](https://github.com/rust-lang/rust/pull/82322#issuecomment-792299734) found that some people are doing this. PRs have been sent to the places that broke, and generally it was agreed that removing the mixing improved the code:
863
+
864
+
> Definitely a good change.
865
+
866
+
> Thanks for spotting that, that was indeed a confusing mix
867
+
868
+
However another instance is in an abandoned project where the repository has been archived, so will not be fixed. And of course if it happened 3 times, there might be more instances in the wild.
869
+
870
+
The interesting pattern boils down to this:
871
+
872
+
```rust
873
+
.map(|v|Ok(something_returning_option(v)?))
874
+
```
875
+
876
+
That means it's using `?` on an `Option`, but the closure ends up returning `Result<_, NoneError>` without needing to name the type as trait resolution discovers that it's the only possibility. It seems reasonable that this could happen accidentally while refactoring. That does mean, however, that the breakage could also be considered "allowed" as an inference change, and hypothetically additional implementations could make it ambiguous in the future. (It's like the normal `AsRef` breakage, and fits the pattern of "there's a way it could be written that works before and after", though in this case the disambiguated form requires naming an unstable type.)
877
+
878
+
This RFC thus proposes removing the accidental interconversions.
879
+
880
+
### Compatibility with accidental interconversions (if needed)
881
+
882
+
If something happens that turns out they need to be supported, the following approach can work.
883
+
884
+
This would take a multi-step approach:
885
+
- Add a new never-stable `FromResidualLegacy` trait
886
+
- Have a blanket implementation so that users interact only with `FromResidual`
887
+
- Add implementations for the accidental interconversions
888
+
- Use `FromResidualLegacy` in the desugaring, [perhaps only for old editions](https://github.com/scottmcm/rust/commit/do-or-do-not-edition)
889
+
890
+
This keeps them from being visible in the trait system on stable, as `FromResidual` (the only form that would ever stabilize, or even be mentionable) would not include them.
891
+
892
+
```rust
893
+
modsadness {
894
+
usesuper::*;
895
+
896
+
/// This includes all of the [`ops::FromResidual`] conversions, but
897
+
/// also adds the two interconversions that work in 2015 & 2018.
898
+
/// It will never be stable.
899
+
pubtraitFromResidualLegacy<R> {
900
+
fnfrom_residual_legacy(r:R) ->Self;
901
+
}
902
+
903
+
impl<T:ops::FromResidual<R>, R> FromResidualLegacy<R> forT {
904
+
fnfrom_residual_legacy(r:R) ->Self {
905
+
<Selfasops::FromResidual<R>>::from_residual(r)
906
+
}
907
+
}
908
+
909
+
/// This is a remnant of the old `NoneError` which is never going to be stabilized.
910
+
/// It's here as a snapshot of an oversight that allowed this to work in the past,
911
+
/// so we're stuck supporting it even though we'd really rather not.
912
+
/// This will never be stabilized; use [`Option::ok_or`] to mix `Option` and `Result`.
@@ -947,19 +991,17 @@ Please also take into consideration that rust sometimes intentionally diverges f
947
991
# Unresolved questions
948
992
[unresolved-questions]: #unresolved-questions
949
993
950
-
Waiting on crater:
951
-
-[ ] Find out more about how widespread the accidental interconversions are, and change the RFC to propose whether to bless/lint/remove them, and how.
952
-
953
994
Questions from T-libs to be resolved in nightly:
954
995
-[ ] What vocabulary should `Try` use in the associated types/traits? Output+residual, continue+break, or something else entirely?
955
996
-[ ] Is it ok for the two traits to be tied together closely, as outlined here, or should they be split up further to allow types that can be only-created or only-destructured?
956
997
957
998
## Implementation and Stabilization Sequencing
958
999
959
1000
-`ControlFlow` is implemented in nightly already.
960
-
- The traits and desugaring could go into nightly (assuming with a confirming crater run) immediately, with implementations for the existing `Option`<->`Result` interconversions.
1001
+
- The traits and desugaring could go into nightly immediately.
961
1002
- That would allow `ControlFlow` to be considered for stabilizating, as the new desugaring would keep from stabilizing any unwanted interconversions.
962
-
- Then the unresolved questions need to be addressed before `Try` could stabilize.
1003
+
- Beta testing might result in reports requiring that the accidental interconversions be added back in old editions, due to crater-invisible code.
1004
+
- Then the unresolved naming & structure questions need to be addressed before `Try` could stabilize.
963
1005
964
1006
<!--
965
1007
- What parts of the design do you expect to resolve through the RFC process before this gets merged?
0 commit comments