This also works with the strict version.
I don't think so...for example, you cannot convert between -128_i8
to 255_u8
with a mask and as
, right?
(The other factor here is the semantics of constant expressions, incidentally. const fn
may provide a path for allowing weird bit manipulation in constants, but that is not certain and will not be stable. Unclear to me just how important this is.)
That's irrelevant. Anly argument involving "masks" in only relevant when it's about converting from wider types. i8 and u8 have the same width. ((!0u64 >> 16) & 0xFF) as u8 works as expected.
And -128i8 would not covert to 255u8 but to 128u8.
The strict version is the only sane thing that can be done. A special case for types of the same size will lead to undiscoverable errors.
@mahkoh Your arguments are very forceful (and in some cases effective), but I wish you'd take more care in your claims, or at least acknowledge when one of your claims was stronger than intended.
I'm referring in particular to this interchange:
It is not irrelevant. The original quoted text from @nikomatsakis was
The appeal of one of pnkfelix's hybrids seems to be that you give up very little -- you can get today's semantics by adding a mask, and you get safety checking in a number of common cases.
Note that @nikomatsakis said "today's semantics".
You directly responded to that with this text:
This also works with the strict version.
But your claim did not hold up for iN <-> uN
conversions, which was @nikomatsakis's point in his response regarding the strict semantics.
I can understand that you regard iN <-> uN
conversions as an inappropriate use of the as
operator. (Not everyone agrees with that, but that is why we are attempting to have a dialogue.)
But if someone is attempting to recover today's semantics in tomorrow's version of Rust, it is useful to know by what means they should do so.
Anyway, I personally am considering reordering my preferences. I think that @glaebhoerl has made a good point that there are two distinct mental operators here (bitcast versus coercion), and maybe we should try harder to find a way to separate how they are expressed.
So youâre saying that the focus was not on âmaskâ but on âall of todayâs semanticsâ. Then tell me how I get todayâs semantics of the following by using (at most) a mask.
let x: A = ..
let y: B = x as B;
You donât, because masks are only useful when you know that youâre casting down.
Since you canât get âall of todayâs semanticsâ, the sensible interpretation of his post is that he was talking about explicit downcasts.
Can you use concrete integer types in your example? Iâm not really clear on what your point is. In any case, what I was trying to say was that if we adopt one of the looser semantics, it is possible to recover the full range of todayâs behavior by using a combination of masks and as
. I donât believe this is true for the strict interpretation, precisely because of edge cases like 128_u8 as i8
(and yes, the exact numbers I used before were incorrect). In particular, one would need to use transmute in some cases to recover todayâs behavior I believe. Do you disagree?
UPDATE: to clarify, when I wrote âIâm not really clear on what your point isâ, I meant: âIâm not clear on what the example is trying to demonstrate.â
given two integer types, one can write a suitable expression.
Not true. http://doc.rust-lang.org/libc/libc/types/os/arch/posix88/index.html
A and B are concrete but unknown integer types.
I did not mean to imply that one can write a single expression that would work for any two sets of integer types, but that given two integer types, one can write a suitable expression.
For some reason I wasnât able to quote your post without repeatedly reloading the page and for some reason my last reply doesnât show up properly. This forum software is so terrible (there are many other issues which I wonât list here) compared to even github issues that I wonât continue to post here. Open an RFC once youâve decided on how you want to clarify the previous RFC.
Not true. http://doc.rust-lang.org/libc/libc/types/os/arch/posix88/index.html
I presume by this somewhat cryptic reference you are intending to say that sometimes, when writing portable or platform dependent code, one does not know the precise integer types you are working with. In that situation, I agree that one cannot recover the precise semantics of as
under any proposal except for making as
work just like it works today. I'm not really sure what this argues for exactly, but it's an interesting scenario I was overlooking.
I do suspect that if we leave
as
the way it is, then it basically serves well as a low-level, type conversion operator, but we'll find that we recommend using other methods of casting (e.g., methods) for converting between integers in safe code in particular.
I think this will not work, similar to how it doesn't work to just claim that int
is not the standard integer type. Make as
do the numerical conversion.
It just doesn't work to say that to_u32().unwrap()
is the correct way to convert numerically when you also have the as u32
which works as long as you're in the right domain (int
also "worked", it still wasn't ment to be the standard).
I'm not really sure what this argues for exactly
To me, this indicates that thereâs probably no middle ground that would allow as
to be used for both numeric and bitwise casts in all cases, so two separate facilities are needed: one for numeric casts, and one for bitwise casts.
If we're going to use the existing as
for one and methods for the other, I think using it for numerical casts is the only consistent approach. All of the arguments that applied to arithmetic seem to apply equally to conversion.
It seems that the vast majority of cases want or can use strict behavior. Looking at your survey of rustc
:
2 & 4: You say these want strict.
1: You say this wants width-oriented to catch dropped bits, but since both values are unsigned, strict works just as well.
3: Doesnât matter
6: Assuming x
is unsigned, (x >> 16 | 0xFF) as u8
would work just fine, even with strict, and makes the intent clearer, in my opinion.
Which leaves only 5 to use the bitwise methods, and explicitly saying âweâre dealing with this value as bitsâ in this instance does not seem undesirable.
In my own code, the vast majority of as
usages want strict, and the rest are byte selection from unsigned values, where strict works just fine with a mask.
Perhaps we could also have some kind of conversion trait that also allowed as
to be used on wrapping types with bitwise behavior.
In my own code, the vast majority of as usages want strict, and the rest are byte selection from unsigned values, where strict works just fine with a mask.
So I'll lay my cards on the table here. I basically agree that strict semantics are what you want most of the time, but I'm still not sure that they should be done through the as
keyword. Rehabiliating the as
keyword feels like a bit of a lost cause. As we kind of settled with the type ascription RFC, as
is supposed to be the "Here be dragons!" conversion operator, so dropping bits doesn't seem totally inappropriate. (*) After all, it also permits a variety of low-level conversions (e.g., *u8
to *u16
) that are pretty unsafe. To top it off, it's not the best syntax and nobody can remember the precedence. Moreover, this seems likely to be a more disruptive change than adjusting the arithmetic operators, and the beta deadline is drawing close (practical considerations matter too). So basically I do have the sense that stabilizing some nice methods for doing "checked" conversions (e.g. the options in NumCast
) might be just fine, and it's certainly less work that lets us focus on other things over the next week or two.
(*) To be clear, I am aware that the Overflow RFC did specify that as
should check. I'm just making the case that we tweak the design. I think this is compatible with the spirit of the RFC, which already divided things up between "math" and "bit" operators. This is just a question of where as
falls (as has been said earlier in the thread).
All that said, Felix and I talked today and he said he was going to do some preliminary investigation into implementing this to try and get a better picture of fallout. I could certainly get behind a stricter as
as well, since as I said I do think that strict semantics are useful most of the time.
As we kind of settled with the type ascription RFC, as is supposed to be the "Here be dragons!" conversion operator, so dropping bits doesn't seem totally inappropriate.
FWIW, this sentiment rings true for me too.
Well, âhere be dragonsâ in the sense of âhere be panicsâ (as opposed to e.g. implicit widening), I thought. The fact that it also does various pointer-related conversions is a good point though.
It seems like low-level pointer reinterpretation should use a transmute
, and not be conflated with integer conversion at all, whether bitwise or numeric. I still think that if we retain as
for any kind of integer conversion, it should use strict semantics. It is the simple, easy to grab conversion operator, and I think it should be treated the same as the arithmetic operators for numeric types. (To be clear, Iâm suggesting that as
be checked (and panic) in debug builds, like the standard arithmetic operators. There should be another facility to get back an option in all builds.)
That said, it almost feels like your post is arguing against as
in general. Maybe we should just deprecate it as is and move itâs varied uses to dedicated functionality. As far as I know, pointer reinterpretation can be handled by transmute
; sized to unsized is handled by coercion (soon to be facilitated by type ascription); and I think bitwise/wrapping casts should be moved to a separate method, like the wrapping arithmetic operations. If we move numeric, checked-in-debug-builds casts elsewhere, is there anything left? (I might be missing a use or two.)
Maybe numeric casts could be implemented using the unary cast
operator Iâve seen discussed elsewhere?