On Casts and Checked-Overflow

I feel like as should not be the keyword for “Here be dragons”, it’s just too short for that IMO.

1 Like

For doing bitcasts we could make the & and | operations take heterogenous arguments. The return type of & is the narrower of the two argument types and the return type of | is the wider of the two. That way, to truncate x: 32 to a u16 you’d write x & 0xffffu16 or x & !0u16.

Ehh, it looks too counter-intuitive for me. One have to guess what x & !0u16 means, it just looks odd and doesn’t do exactly what you expect it to do. With as u16 it reads naturally and has obvious semantics.

This is roughly what I've been thinking, though I would not advocate the use of transmute. I think the missing variable is the fate of const fn. We've talked many times about removing as in favor of more specialized methods, but settling the story around const fn was always the blocker. My inclination right now is to leave as the way it is. I do not consider it deprecated, as there is not yet a suitable replacement, but I think that we should build up better replacements and perhaps we will be able to deprecate it in the near future. This might be the cast operator, which I confess I was fond of, but I'd be happier if we can do it with methods rather than adding a new operator.

(As for numeric casting, I could imagine having two sets of methods, one that is only for widening, and hence infallible, and some variant on the existing NumCast, which permits arbitrary checked conversions.)

To clarify: I do not like transmute because it is a huge hammer. Sometimes it’s the hammer you need, of course. This is also a point of continuing disagreement between me and Alex/Gankro/etc, because in some ways having just transmute is much nicer than a family of functions that don’t really cover all use cases. It’s just that I have fixed a lot of bugs that went undetected due to errant transmutes, so I’ve learned to be wary.

For better or worse, that is its role. After all, we recently introduced lints that push you to only use as for semantically dangerous casts.

To clarify: I hate transmute. It is the biggest scariest hammer there is. And it's using return-type inference to figure out what to do, too! All the uses of transmute I see are basically legacy code from before as or trying to paper over janky bits in our type system (inability to be general over &T -> &U and &mut T -> &mut U, lack of ergonomic typestate, struggling with equivalent-but-not-really values, being able to talk about a value as "just" bits, etc).

I think we might need three different numeric conversions, here: one that only widens (I strongly believe this should be an operator or set of methods, not happen implicitly), one that always does a checked conversion and returns an option, and one that functions analogously to the arithmetic operators, panicking for lossy conversions in debug builds and truncating in release builds. These would, of course, be in addition to a bitwise cast.

I'd be fine with methods. For bikeshedding purposes, what about cast for panicking in debug builds, checked_cast for returning an option, widen for guaranteed-lossless widening, and truncate for bitwise casting? I think it would be convenient for all of them to infer their target type, and type ascription can be used where being explicit is desirable.

1 Like

This seems like a stylistic issue, as there's certainly nothing preventing one from using explicit types (e.g., transmute<*u8, *u16>(val) or transmute(val): *u16 with type ascription, to go with the previous example). Maybe this should be a lint?

This is roughly what I was thinking too -- in particular separating widening and narrowing. Seems like good fodder for a (perhaps series of...?) RFCs.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.