Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversions: FromLossy and TryFromLossy traits #2484

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

dhardy
Copy link
Contributor

@dhardy dhardy commented Jun 22, 2018

Add FromLossy, TryFromLossy traits.

Discuss the bigger picture of conversions and the as keyword.

Specify that From implementations must not only be safe, but also exact.

Rendered RFC

@dhardy dhardy changed the title New RFC: from-lossy Conversions: FromLossy and TryFromLossy traits Jun 22, 2018
@Centril Centril added the T-libs-api Relevant to the library API team, which will review and decide on the RFC. label Jun 22, 2018
@Centril
Copy link
Contributor

Centril commented Jun 22, 2018

I think the word exact is problematic and that it defined imprecisely in this RFC.

It suggests to me that for X: From<Y> there must exist an inversion Y: From<X> such that X and Y are isomorphic. But u8u16 does not have an inversion u16u8 precisely because it is a narrowing operation.

Since From is such a central trait in the ecosystem, I think the requirements and guarantees placed on implementations of it should be specified with mathematical precision. As currently specified, I am not sure how the changed definition extends to types outside the standard library.

One possible definition that seems to fit the criteria described in the RFC is:

  • [X: From<Y>] → [∀ y0, y1 : Y. X::from(y0) ≡ X::from(y1) → y0 ≡ y1].

@Centril
Copy link
Contributor

Centril commented Jun 22, 2018

Other questions that this RFC provoked:

  • Should From::from always be a "pure" computation?
  • Is such a change in recommendation re. From considered to be a breaking change?

@SoniEx2
Copy link

SoniEx2 commented Jun 22, 2018

should From be allowed to allocate remote resources? (granted you should probably use TryFrom for things like that)

@dhardy
Copy link
Contributor Author

dhardy commented Jun 22, 2018

It suggests to me that for X: From there must exist an inversion Y: From such that X and Y are isomorphic

I disagree with the need for an isomorphism; e.g. I see no problem with f32's two representations for 0 both mapping to the same integer value.

To be fair, that's not what you said. But surely an exact conversion f: A → B implies only that for any a1, a2 in A, a1==a2 exactly when f(a1) == f(a2) (and derived from this: if a==b can be defined for a in A and b in B, then a==f(a) for all a in A). Okay, that's roughly what your type rule says, if I parse it right (the . in the middle is confusing; I've usually seen | used (maths contexts); also I believe we need a two-way implication).

Should From::from always be a "pure" computation?

Meaning an intrinsic? I don't really see why this is important.

Is such a change in recommendation re. From considered to be a breaking change?

Since this wasn't specified before, I don't see it as a big issue, but I suppose it could be (if libs remove their implementations to comply).

should From be allowed to allocate remote resources?

It musn't fail (existing requirement, not from this RFC). Exactly what this means with regards to unlikely (and probably unrecoverable) panics is up for interpretation, I guess (and specifying something won't necessarily influence how people use it for anyway).

@Centril
Copy link
Contributor

Centril commented Jun 22, 2018

@dhardy To be clear, I'm not saying that X: From<Y> must also imply Y: From<X>, so I don't think there should be an isomorphism requirement either. I'm just saying the way you framed it suggests that to me.

Okay, that's roughly what your type rule says

That's exactly what the proposition says :)

In other words:

 X: From<Y>   X::from(y0) ≡ X::from(y1)
--------------------------------------- proper_From_implementation
                 y0 ≡ y1

or if we are inclined to be somewhat more pedantic:

Δ ⊢ Implemented(X, From<Y>)
Γ ⊢ y0 : Y
Γ ⊢ y1 : Y
Γ, Δ ⊢ X::from(y0) ≡ X::from(y1)
--------------------------------- proper_From_implementation
Γ, Δ ⊢ y0 ≡ y1

Meaning an intrinsic? I don't really see why this is important.

No, "pure" means deterministic here. That is (formulated somewhat imprecisely), for
any two y0, y1 : Y, y0 ≡ y1 → X::from(y0) ≡ X::from(y1). So X::from could have no side-effects.

I don't such a requirement is necessary tho?

@newpavlov
Copy link
Contributor

It's not just about this RFC, but can't we make FromFoo traits just aliases for TryFromFoo<Error=!>?

@dhardy
Copy link
Contributor Author

dhardy commented Jun 22, 2018

@Centril someone likes their type theory 😄

WP has an article on pure functions. But it's not the same as what you wrote which is about from respecting the operator (presumably in this case PartialEq, which is not required to be implemented for either type). Or if by you mean "the same binary representation" or something, I don't think that's a distinction worth making; it's not something we have any operator to test (aside from indirectly with memory comparisons), and it would be possible (though wrong) for PartialEq to depend on other things such as the memory alignment.

Back on topic, I guess all from implementations should be pure (ignoring allocations / caches), but I don't think specifying this in the documentation is useful in any way.

@newpavlov Good point. I'm not sure, is it possible to do this without it being a big breaking change?

@Centril
Copy link
Contributor

Centril commented Jun 22, 2018

@dhardy

someone likes their type theory 😄

I do! ;) I think we should specify formally (with logic -- probably not typing rules) in the RFC and the documentation of From what laws should hold and what is meant by "exact", as we've done with PartialEq.

Yeah being imprecise about what exactly is is what I meant about "formulated somewhat imprecisely" ;)

I guess all from implementations should be pure (ignoring allocations / caches), but I don't think specifying this in the documentation is useful in any way.

If that is a reasonable expectation the user has, we might as well specify it?

@newpavlov
Copy link
Contributor

newpavlov commented Jun 22, 2018

@dhardy
I am not sure, I guess it will depend on how trait aliases and never type will shape out. For example if impl's on trait aliases will be allowed, then current implementations of From will be still legal and without blank implementations of TryFrom for From we should be fine. And of course we will need automatic coercion (or straight equivalence maybe?) of Result<T, !> to just T. (I am not sure how folks who work on it think it should work) But either way backward compatibility concerns only From and Into traits, not the traits described in this RFC.

UPD: Hm, it looks automatic coercion is not planned, and it seems we'll get back to cyclic trait design from rand discussions:

pub trait TryFrom<T>: Sized {
    type Error;
    fn from(value: T) -> Self {
        Self::try_from(value).unwrap_or_else(|_| panic!("error"))
    }
    fn try_from(value: T) -> Result<Self, Self::Error> {
        Ok(Self::from(value))
    }
}
trait From = TryFrom<Error=!>;

It will allow existing From implementations work as is, though it definitely looks quite hacky.

Copy link

@hanna-kruppe hanna-kruppe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't formed an opinion on the actual proposal, so here's what I do best: nitpicking about floating point minutiae

This has several problems:

- `as` can perform several different types of conversion (as noted) and is therefore error-prone
- `as` can have [undefined behaviour](https://github.com/rust-lang/rust/issues/10184)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this is "just" an implementation bug, thus "temporary" and not a problem with as-the-language-feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, the undefined behaviour can be (and is being) fixed. I should say instead that there are certain casts which have no correct result and therefore should fail, which as has no mechanism for (other than panics).


Where conversions are not exact, they should be equivalent to a conversion
which first forces some number of low bits of the integer representation to
zero, and then does an exact conversion to floating-point.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this trying to avoid specifying a rounding mode? Or is it trying to specify a rounding mode in some convoluted manner? Either way, all these conversions should be specified to round to nearest, ties to even. That's the default rounding mode and we (as well as other languages) use it everywhere except for float -> int conversions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, this is convoluted. And you're correct, since the output type here is floating-point, it would be expected to round to nearest with ties to even.


The implementations should fail on NaN, Inf, negative values (for unsigned
integer result types), and values whose integer approximation is not
representable. The integer approximation should be the value rounded towards

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failing if the integer approximation doesn't fit the target type is... debatable. The proposed semantics for as are to saturate to int_type::MAX or int_type::MIN respectively, and that is in some ways an "approximately equivalent value".

BTW the same point applies to infinities (i.e., it's reasonable to define f32::INFINITY -> u8 to result in 255), but I can understand being more uneasy with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, infinities and out-of-range values fall into the same category here: not representable. IMO 255 is a very poor approximation of 1000f32 and an even worse approximation to 1e300f64.

Certainly I'm being opinionated here but I think if we have a fallible conversion available then we should fail on out-of-range values. (But if not, then TryFromLossy also has no reason to exist.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have sympathy with that argument, but at the same time I'd like to reduce divergence from the semantics of as (to avoid people sticking to as because they prefer its behavior), and currently that seems more likely to be saturation than panicking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that as must return some defined value simply in order to avoid undefined behaviour. We have no need to model this conversion trait on the limitations of as.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as doesn't have to return a value, it can panic (there are some reasons to not want that, but it avoids UB and is reasonable in isolation). That would be the moral equivalent of this trait returning Err.

- 100_000f32 → u16: error

(Alternatively we could allow floats in the range (-1, 0] to convert to 0, also
for unsigned integers.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems more consistent with the stated rounding mode (I view all these conversions as "round, then see if they fit into the target type") and it also matches the current behavior of as.

negative, infinite or an NaN. So even though the output type has large enough
range, this conversion trait is still applicable.)

The implementations should fail on NaN, Inf, negative values (for unsigned

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "negative values" include negative zero?

(This question disappears if values in (-1.0, 0.0] result in Ok(0) as discussed below.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, not last time I checked 😄

But this question goes away if we use the alternative as you suggested anyway.

representable. The integer approximation should be the value rounded towards
zero. E.g.:

- 1.6f32 → u32: 2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't rounded towards zero.

@dhardy
Copy link
Contributor Author

dhardy commented Jun 23, 2018

Updated regarding several things mentioned.

@SimonSapin
Copy link
Contributor

  • I agree that as is problematic and like the idea of adding alternatives. (Maybe deprecating it some day, but this RFC can be incremental improvement without going that far.)

  • Regarding naming: I find the world lossy to be a bit vague. What kind of loss are we talking about? It turns out that this RFC is all about approximating numeric types, so maybe Approximate and TryApproximate would work as trait names? Verbs are nice for single-method traits because they also work well for the method name.

  • Regarding the prelude: we have precedent with TryFrom and TryInto of adding unstable traits and saying “we’ll consider adding them to the prelude when stabilizing”. When we did, a number of crates broke because they had copy/pasted the traits into their own code in order to use them on stable Rust, and now had two traits in scope with methods of the same name, causing a compiler error. We had to revert: Revert "Add TryFrom and TryInto to the prelude" rust#49518

    I think the lesson here is that when discussing new traits here and there’s a chance that we’ll want them in the prelude, we should add them to the prelude as soon as they are implemented even if we end up removing them before stabilization.

    However if we make an API that works without traits being in scope, being in the prelude doesn’t matter. There is precedent for this with the str::parse inherent method and the FromStr trait. Since this RFC is about numeric approximation, it is concerned with a comparatively small number of types (14, v.s. unbounded for the more general From and TryFrom traits). We could have inherent methods on the relevant numeric types, similar to str::parse.

@Centril
Copy link
Contributor

Centril commented Jun 23, 2018

@repax The documentation of From should explain what injective means for those not familiar with that mathematical jargon :)

But yes, saying that T::from must be an injective function should be sufficient since to be a function in the first place, T::from must satisfy ∀ s0, s1 : S. s0 ≡ s1 → T::from(s0) ≡ T::from(s1) since a function satisfies ∀ x, y : S. x ≡ y → f(x) ≡ f(y) by definition.

The link to PartialEq is not something testable for all From implementations since there is no requirement that X: From<Y> → [X: PartialEq ∧ Y: PartialEq] so idk about that part...

But regardless, it is nice to see a movement towards more exact definitions, kudos to @dhardy on that.

@dhardy
Copy link
Contributor Author

dhardy commented Jun 23, 2018

@Centril of course all functions are homomorphisms w.r.t. equality (whoops). I guess we can drop the PartialEq stuff then.

@SimonSapin thanks for the feedback.

  • Yes, the approximations could be inherent methods, though it is much easier to write generic code using traits (one of the motivations here).
  • I guess these could be named Approximate etc., though isn't the parallel with the name From useful?

I suppose there should be Into parallels too.

@SimonSapin
Copy link
Contributor

it is much easier to write generic code using traits (one of the motivations here).

I’m having a hard time imagining a situation where generic code with these traits in a where clause is useful. Do you have some examples?

If I remember correctly the existence of both From and Into has to do with impl coherence. In some combinations of types one can be implemented but not the other. I don’t think this is as much of a concern here since I don’t expect many impls outside of the standard library.


rust-lang/rust#42456 was another attempt that could be mentioned in Prior Art, even though it didn’t land.

@burdges
Copy link

burdges commented Jun 23, 2018

Adding traits sounds unnecessarily confusing and hierarchical. And maybe breaking. Could const members address this? And avoid breakage via default values?

pub trait From<T> {
    fn from(T) -> Self;
    const INJECTIVE: Option<bool> = None;
    const SURJECTIVE: Option<bool> = None;
}

If you needed this, then you'd write where U: From<T, const INJECTIVE = Some(true)> or whatever.

@dhardy
Copy link
Contributor Author

dhardy commented Jun 24, 2018

@SimonSapin this is what motivated me. Ideally we wouldn't need to implement this trait ourselves. But it may not have wide usage.

@burdges interesting idea. But why use Option and why mention SURJECTIVE? And if we can have const INJECTIVE: bool = true; then does U: From<T> imply U only matches injective ("non lossy") implementations? If so then this might be workable. (But would it also be possible to match any implementation, e.g. with U: From<T, INJECTIVE = _>?)

@SimonSapin
Copy link
Contributor

@dhardy This example is sort of making my point. As far as I can tell it is not generic, and $ty is (after macro expansion) a concrete type at every $ty::cast_from_int(…) call site.

Is there a use case for code like either of these?

fn foo<N>() where N: CastFromInt<u32> {}
fn bar<N>() where u32: CastFromInt<N> {}

@burdges
Copy link

burdges commented Jun 24, 2018

I think const INJECTIVE: Option<bool> = None; keeps existing impl From<..> for .. code correct by not specifying either true or false incorrectly. I donno if SURJECTIVE helps, maybe not.

@dhardy
Copy link
Contributor Author

dhardy commented Jun 24, 2018

Perhaps not. So I guess the alternative is simple but involves quite a few methods: add these to every integer type:

fn to_f32(self) -> f32;
fn to_f64(self) -> f64;

These types already have to_le and to_be functions and proposed to_bytes, so I guess these fit.

Or more akin to str::parse, add this to f32 and f64 (along with a trait):

fn from_int<T: ToFloat<Self>>(x: T) -> Self;

But @burdges U: From<T, SURJECTIVE = None> (i.e. the default) will not match From<T, SURJECTIVE = Some(false)> implementations, so it becomes a breaking change to specify these constants in existing implementations (and a pain to use generically).

@burdges
Copy link

burdges commented Jun 24, 2018

We do nee a default regardless. I suppose you're saying const INJECTIVE: bool = false; can be the default because code should never use U: From<T, SURJECTIVE = false>? Fine.

If you want to avoid breaking changes then you can specify it like this:

/// Opaque type used to specify if a `From` is injective.
pub struct Injectivity(bool);
const INJECTION: Injectivity = Injectivity(true)

pub trait From<T> {
    fn from(T) -> Self;
    const INJECTIVE: Injectivity = Injectivity(false);
}

pub trait Into<T>: Sized {
    fn into(self) -> T;
    const INJECTIVE: Injectivity = Injectivity(false);
}
impl<T, U> Into<U> for T where U: From<T>
{
    fn into(self) -> U {
        U::from(self)
    }
    const INJECTIVE: From::Injectivity = <U as From>::INJECTIVE;
}

@Diggsey
Copy link
Contributor

Diggsey commented Jun 24, 2018

@burdges adding constants like that seems way more confusing (and also breaking) than what is proposed in the RFC.

IMO it was a mistake to add From/To traits in the first place without specifying a meaning beyond what their signature implies. Luckily, in practice usage of these traits has followed the rules set down in this RFC (that conversions with these traits should be lossless).

Having more traits is fine, but I think it would be useful to reduce the number of traits which must be implemented, so I think going forward all new infallible traits should have a generic implementation for types implementing the infallible trait with an error type of !:

impl<T, U> FromLossy<T> for U where U: TryFromLossy<T, Error=!> { ... }

Also, @dhardy, the trait definitions in the RFC are missing their generic parameter T - I believe this is a mistake?

@burdges
Copy link

burdges commented Jun 24, 2018

I imagined the coherence rules isolated any breakage when adding a default constant, but.. I suppose you're talking about how the trickier coherence rules around the existing type parameter, yes?

I suggested constants here largely because they permit positive or indeterminate defaults, while traits mostly only support negative defaults. It's possible negative defaults are desirable here, but the RFC design looked kinda bent around negative defaults. I think folks have voiced roughly that opinion, hence my initial suggestion being a default None interpreted as unspecified.

As an aside, constants are less confusing because they're documented with the main trait, but rust doc could be modified to have subordinate traits documented on the same page as the main trait, so whatever.

Oh. If one really wants unspecified defaults, one might also define trait FromProperties<T> : From<T> { const INJECTIVE : bool }, but that's messier.

@dhardy
Copy link
Contributor Author

dhardy commented Jun 25, 2018

@Diggsey what's your take on @SimonSapin's point that it may be better to find options which don't involve adding to the prelude? FromLossy is more generic than what he proposed but is not the only option.

Auto-implementing TryFromLossy for all FromLossy impls makes sense. Doing the same for TryFrom / From is breaking (for any explicit implementations of both) but, once specialisation is ready, we should be able to introduce a default implementation. There's one caveat here: introducing usage of default on an existing implementation is (technically) a breaking change, so either we should not auto-implement TryFromLossy before specialisation, or we should put up with slightly different semantics there.

@Diggsey
Copy link
Contributor

Diggsey commented Jun 25, 2018

It's a good point - I would at least like a solution which is generic over the numeric types (including user-defined numeric types) but I don't think it need be more general than that.

Regarding the prelude - would it be possible to have the traits exist in both the prelude and a module, and only stabilise the version outside the prelude? (Effectively preventing the kinds of conflicts @SimonSapin described without committing to stabilisation of the trait's existence in the prelude).

TBH, I'm not sure these need to be in the prelude when the as operator still exist, as that will cover the most common uses.

@vks
Copy link

vks commented Sep 1, 2018

one would think it could be solved by by implementing SliceIndex<[T]> for I where usize: From.

I don't think this this is feasible, because it breaks type inference, likely for a lot of code.

@dhardy
Copy link
Contributor Author

dhardy commented Sep 1, 2018

True, that wouldn't work because of type inference.

What might work is (a) trait Implicit<S>: From<S> {} selectively enabling implicit conversions plus (b) changes to type inference such that if the type is not directly specified (x: T) then there must be a unique type among input types (x = f() where f -> I) or, failing that, output types (g(x) where g(y: O)), such that all bounds are met (T: Implicit<I> and Implicit<O>: T).

But annoyingly literals need special attention to avoid breakage.

@dhardy
Copy link
Contributor Author

dhardy commented Jul 9, 2019

So, lets try to revive some of the discussion here...

Of course, we could reduce this list by means of a rounding_mode parameter, but that's less convenient to use much of the time.

Alternatively, there could be an ApproxScheme type parameter, see conv::ApproxFrom. However, this is prone to result in unportable code. I think it is best to have explicit methods as you suggested.

This is an interesting solution. I think the reason for the compile error is just because ApproxFrom has an associated type? There is also a lesson here: ApproxFrom has a lot of implementations (only considering the primitive types).

We could do something similar using wrapper types as above:

/// pure marker wrapper
pub struct Round<T>(T);

impl TryFrom<Round<f32>> for u32 { ... }

assert_eq!(u32::try_from(Round(13.7f32))?, 14);

One could achieve a similar effect with the existing rounding methods, but could we optimise it to similar code?

let x = u32::try_from(13.7f32.round())?;

let i: u16 = ...;
my_slice[i as usize] = 0;

This is a numeric conversion supported by From. usize::from(i) works; i.into() doesn't (the slice impls Index<I> where I: SliceIndex<[T]> which includes usize, Range, RangeFull, etc.).

There's a related case: let i: u32 = ...;. In this case From is not supported; TryFrom is, but (1) this is not currently in the prelude so must be manually imported and (2) usize::try_from(i).unwrap() is much less ergonomic.

Making safe conversion as ergonomic as via the as keyword is a tall order! Given how common this is, it might be worth adding a specialist trait with a function like fn as_usize(self) -> .... To make error handling ergonomic, there are only two options: panic-on-error, or add syntactic-sugar like ?! for .unwrap().

Alternatively, it seems @leonardo-m is hinting that we should have a language-level solution which does not have the drawbacks of as... it seems such a feature would be quite difficult to introduce.

There are also enums

As mentioned, there seems to be no reason that one can't implement From<T> for B for each enum T with base type B. Alternatively std could add a new trait for enum conversions. This goes beyond the scope of this RFC.


let x: u32 = ...;
let y = (x % 10) as u8;

This is just an optimiser problem IMO, and not common enough to worry about ergonomics.

But the slice <-> array conversions is currently too much underpowered, tricky and unergonomic

Fundamentally the length is part of an array's type and a slice's value, so a library solution can never omit the error check from slice[..4].into::<[_; 4]>(). If one must have an ergonomic type-safe conversion, then panic-on-error is probably the way to go (but not with From which is only for infallible conversions).

The only option for proving correctness at compile-time is language-level support via some new syntax (e.g. slice[..4] as [_; 4]).

Both these examples (and many others) desire type-safe conversion which should be provably-correct, but where proof is beyond the capabilities of language syntax + the type system. To address these, we could add a method to TryFrom:

pub trait TryFrom {
    type Error;
    fn try_from(value: T) -> Result<Self, Self::Error>;

    #[expect_success]
    fn do_from(value: T) -> Self;
}

where do_from will panic-on-error but the new expect_success attribute causes the compiler to generate a warning if it is unable to prove that the method will not panic. (Or we could just leave out the attribute.)


@camelid
Copy link
Member

camelid commented Nov 27, 2020

I definitely like (soft-)deprecating as's use for lossy conversions. I feel like as should only be used for two things:

  • safe, exact casts (u8 -> u16, maybe f32 -> f64?)
  • casts between raw pointers or casts from functions to raw pointers (or other related casts)

Perhaps we could warn on as being used for lossy conversions and then switch to a hard error for the 2021 (or later) edition?

@scottmcm
Copy link
Member

scottmcm commented Nov 28, 2020

With my lang hat on, that (a hard error on lossy as) is a big enough change that there's no way it's happening as soon as the 2021 edition. (Even though, as you can see elsewhere in the thread, I'm a fan of that general direction.)

The lint does exist in clippy, but even there it seems to be allow-by-default: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless

@SimonSapin
Copy link
Contributor

Perhaps we could warn on as being used for lossy conversions and then switch to a hard error for the 2021 (or later) edition?

No.

2021 or not, I feel that a lint against as (while useful!) should never be switched to error by default, let alone a hard error. Yes as is ambiguous and would often be better replaced by more explicit APIs (ignoring for a moment that those APIs mostly don’t exist yet). But forcing that transition on existing code based would just be a barrier for adoption of a new edition. And for what benefit? Many of these existing as expressions are in fact correct.

@dhardy
Copy link
Contributor Author

dhardy commented Mar 18, 2021

A small subset of you might be interested in my new library, easy-cast. It attempts to replace most uses of as in for numeric casts (though not all), and covers a small sub-set of what this RFC proposed.

geky added a commit to geky/gf256 that referenced this pull request Nov 8, 2021
…ssy conversions

See: rust-lang/rfcs#2484

Note due to blanket-trait limitations, we can really only implement this
for new types in the current crate
@jonaspleyer
Copy link

For anyone still interested on this topic: It just came up again in a thread about Rusts ergonomics around numerical computations.

https://internals.rust-lang.org/t/rust-and-numeric-computation/20425/14

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-conversions Conversion related proposals & ideas Libs-Tracked Libs issues that are tracked on the team's project board. T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
Status: Rejected/Not lang
Development

Successfully merging this pull request may close these issues.