Skip to content

Meta-ACP: Integer cast methods #833

Description

@clarfonthey

This is based upon the meeting discussion on these various active tracking issues:

And these ACPs: (just one atm)

And these stable methods:

I've generated a table which unfortunately has to be raw HTML because GHM tables are fragile and awful. (Meta point: if you want to make your own table you can just ignore the top row and post it as GFM if it's difficult otherwise. I just wanted to have the nicer headers.)

Hopefully things are easy to read, but specifically for the sizes, the notation of i2N and u2N are used to indicate which integer in a conversion is larger, and X indicates a conversion that is not possible. (Note that 2N is a placeholder here and it's fine to do higher powers of two, e.g. iN → i2N includes i8 → i32, etc.)

The remaining cell contents indicate either whether an operation is lossless or the rounding mode if it's lossy. Note that "default" refers to the default overflow behaviour and not the default casting behaviour, since the default casting behaviour is always wrapping whereas the default overflow behaviour panics in debug mode.

trackedsigned ↔ signedunsigned ↔ unsignedsigned ↔ unsigned
i2N→iNiN→iNiN→i2Nu2N→uNuN→uNuN→u2Ni2N→uNiN→uNuN→i2NuN→iNu2N→iN
widen154330XnoopnoopXnoopnoopXXXXX
truncate154330wrapnoopXwrapnoopXXXXXX
saturating_truncate154330saturatenoopXsaturatenoopXXXXXX
checked_truncate154330checknoopXchecknoopXXXXXX
saturating_cast157388saturatenoopnoopsaturatenoopnoopsaturatesaturatenoopsaturatesaturate
wrapping_cast157388wrapnoopnoopwrapnoopnoopwrapwrapnoopwrapwrap
checked_cast157388checknoopnoopchecknoopnoopcheckchecknoopcheckcheck
strict_cast157388strictnoopnoopstrictnoopnoopstrictstrictnoopstrictstrict
unchecked_cast157388UBnoopnoopUBnoopnoopUBUBnoopUBUB
castlibs811defaultnoopnoopdefaultnoopnoopdefaultdefaultnoopdefaultdefault
checked_cast_signed154650XXXXXXXXXcheckX
saturating_cast_signed154650XXXXXXXXXsaturateX
strict_cast_signed154650XXXXXXXXXstrictX
cast_signedstableXXXXXXXXXwrapX
checked_cast_unsigned154650XXXXXXcheckcheckXXX
saturating_cast_unsigned154650XXXXXXsaturatesaturateXXX
strict_cast_unsigned154650XXXXXXstrictstrictXXX
cast_unsignedstableXXXXXXXwrapXXX

Adding notes in a few dedicated sections:

Truncate naming bikeshed

We used "narrowing" a lot in the meeting and I don't think this is necessarily the right name, but I'm going to use "narrow" just because the connotation of "truncate" is very confusing.

Personally, I think that "shrinking" would be better than narrowing since we're talking about the range of the type, although this is mostly a hill I don't think we should die on.

Note that naming wrapping_narrow versus wrapping_shrink is the main point of contention, since otherwise methods like saturating_narrow and saturating_shrink seem fine. We could also just accept that wrapping_narrow is truncate and use e.g. saturating_narrow for everything else.

Widen/Narrow overlap

Right now, widen and narrow overlap on same-type conversions; both allow identity conversions, I think this probably shouldn't be the case. To me, it would make sense for widen to always be a lossless conversion and for narrow to always be a lossy conversion, which means that widen would allow >= size but narrow would require < instead of <= (current behaviour).

Widen/Narrow signed/unsigned interplay

Right now, widen/narrow never change signedness, but I think this should be allowed for conversions which strictly increase or decrease the range of the type. So, in our table, we would allow uN → i2N for widening unsigned to signed integers, and i2N → uN for truncating signed to unsigned integers, but everything else remains the same. (Note: iN → uN would be adding range, so, not strictly narrowing the range.)

Missing narrowing/explicit signedness conversions

narrow is missing strict, unchecked, and a default overflow version; the default is currently wrapping, which should probably be renamed to wrapping_narrow. See bikeshed above for more info on that particular method.

This also applies for the explicit _signed and _unsigned methods, although more on those later.

"Default" overflow method

There is some genuine confusion to be had with the default overflow method not being the default casting method (as), and maybe it might be worth considering a change to this on an edition boundary to pair with this. That would require lang to get involved, but personally, it makes sense that x.cast() and x as _ do the same thing and that is not what we're proposing at the moment, unless we were to propose that on an edition boundary and just make x.cast() do edition-default behaviour.

(From my understanding, this is feasible, just, it would require an RFC and a lot of questions to be resolved on how to configure this. My guess is it would just split off of overflow checks into a separate flag to be configured, and we would have to explicitly decide how we want to configure that. Note that the main proposal here is this being a cargo edition, not a crate edition, with cargo defaulting the flag for the debug/release profiles and not different crates having different overflow behaviours, which feels wrong.)

As an alternative to (gestures) we could just call this "lax" and name accordingly. (But, I think lax_cast instead of cast is annoying.)

Explicit signedness methods

Personally: I don't like these at all. The way it currently works is the casts normally allow all interplay between signedness, and the dedicated signedness methods only work for integers of the same size.

I would instead expect this to actually be reversed: instead of having cast_signed methods on unsigned and cast_unsigned methods on signed, have cast_signed methods on signed and cast_unsigned methods on unsigned which explicitly prevent the signedness from changing. To me, there doesn't seem much value in limiting the casts otherwise, since if there can be rounding going from i32 to u32, then I don't know why you don't let me go from i32 to u16, for example. The only argument against would be having to specify the type, but I kind of expect that to never matter since the only real case we're optimising for is e.g. being able to cast from uN to iN, perform some maths, then cast back to uN, which… what are you even doing?

My recommended table

This is based upon the combination of feedback above, and what I think would be best to stabilise. Mostly to reference for later. It removes the tracking issues.

It does not resolve the narrowing naming bikeshed.

Differences from above table are marked ❗.

signed ↔ signedunsigned ↔ unsignedsigned ↔ unsigned
i2N→iNiN→iNiN→i2Nu2N→uNuN→uNuN→u2Ni2N→uNiN→uNuN→i2NuN→iNu2N→iN
widenXnoopnoopXnoopnoopXXnoop❗XX
saturating_narrowsaturateX❗XsaturateX❗Xsaturate❗XXXX
wrapping_narrowwrapXXwrapXXwrapXXXX
checked_narrowcheckX❗XcheckX❗Xcheck❗XXXX
strict_narrowstrictXXstrictXXstrictXXXX
unchecked_narrowUBXXUBXXUBXXXX
narrowdefault❗X❗Xdefault❗X❗Xdefault❗XXXX
saturating_castsaturatenoopnoopsaturatenoopnoopsaturatesaturatenoopsaturatesaturate
wrapping_castwrapnoopnoopwrapnoopnoopwrapwrapnoopwrapwrap
checked_castchecknoopnoopchecknoopnoopcheckchecknoopcheckcheck
strict_caststrictnoopnoopstrictnoopnoopstrictstrictnoopstrictstrict
unchecked_castUBnoopnoopUBnoopnoopUBUBnoopUBUB
castdefaultnoopnoopdefaultnoopnoopdefaultdefaultnoopdefaultdefault
saturating_cast_signedsaturate❗noop❗noop❗XXXXXXX❗X
wrapping_cast_signedwrapnoopnoopXXXXXXXX
checked_cast_signedcheck❗noop❗noop❗XXXXXXX❗X
strict_cast_signedstrict❗noop❗noop❗XXXXXXX❗X
unchecked_cast_signedUBnoopnoopXXXXXXXX
cast_signeddefault❗noop❗noop❗XXXXXXdefault❗X
saturating_cast_unsignedXXXsaturate❗noop❗noop❗X❗X❗XXX
wrapping_cast_unsignedXXXwrapnoopnoopXXXXX
checked_cast_unsignedXXXcheck❗noop❗noop❗X❗X❗XXX
strict_cast_unsignedXXXstrict❗noop❗noop❗X❗X❗XXX
unchecked_cast_unsignedXXXUBnoopnoopXXXXX
cast_unsignedXXXdefault❗noop❗noop❗Xdefault❗XXX

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions