7 ms·
> Unlike C, Rust won’t automatically promote integer types to wider types. It forces you to manually cast any mismatching integer types for every operation > R
by VWWHFSfQ 4y ago
> Unlike C, Rust won’t automatically promote integer types to wider types. It forces you to manually cast any mismatching integer types for every operation
> Rust's insistence on manual casting everywhere encourages people to write inefficient code, because writing verbose code feels uncomfortable and adds friction.
This reads a lot to me like "we like how easy it is to create bugs in C. We'd like to be able to do that in Rust, too." But maybe I'm misunderstanding. I'm sure these people know a lot more about the consequences of implicit integer casting than I do.
- colejohnson66 4y agoYes, but: > ..there's no reason why you couldn’t safely promote a u8, u16, or u32 into a usize, and asking programmers to manually cast integers everywhere makes the code more noisy and verbose. They have a valid point. In C#, numerical casts that won't lose precision are implicit, and any that could are explicit. I think that’s a nice compromise. So, int to uint requires a cast, but int/uint to long doesn't: int myInt = /* ... */; long myLong = myInt; uint myUInt = myInt; // error CS0266: Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?) ulong myULong = myInt; // error CS0266: Cannot implicitly convert type 'int' to 'ulong'. An explicit conversion exists (are you missing a cast?) uint myUInt = /* ... */; int myInt = myUInt; // error CS0266: Cannot implicitly convert type 'uint' to 'int'. An explicit conversion exists (are you missing a cast?) long myLong = myUInt; ulont myULong = myUInt;
- stonemetal12 4y agou8 max value + 1 means something as a u8. u8 max value + 1 means something different as a u16. Implicitly changing behavior is never cool.
- colejohnson66 4y agoThat wouldn’t be an issue. The whole claim is that this: let a: u8 = /* */; let b: u16 = a; push_u16(a); can easily be inferred into: (no pun intended) let a: u8 = /* */; let b: u16 = a.into(); push_u16(a.into()); But the Rust language has decided that implicit upconversion that is already apparent is a bad idea because “bugs”. And I’d agree with them, but it’s stupid in this case. The issue at hand isn’t asking for ‘a’ to be upconverted to a u16 in the middle of an expression like this: let b: u16 = a + 1; …because you’d be left with the question of when to upconvert (“implicitly changing behavior” as you call it). No, it would only be when it shouldn’t need to be said such as a lone assignment or function call. And if the Rust language’s argument is that it’d be unclear when it’s allowed? Because I’ve seen many RFCs where someone does what you just did: “what about this weird corner case where ‘u8 max + 1’? Should that be a u8 or u16? Maybe we should toss the whole proposal!” To that I’d say: start off where it’s clear (like my examples above). It’s not a backwards incompatible change. /rant
- shepmaster 4y agoAnd chiming in: > there's no reason why you couldn’t safely promote a u8, u16, or u32 into a usize The people running Rust on a 16-bit architecture (e.g. AVR) would disagree. On those platforms, usize is 16-bit, so promoting u32 to usize would not be possible. I _do_ wish there was a nicer way to declare up-front "I *never* want this crate to compile on something smaller than 32-bit (or 64-bit), so assume that". IIRC, similar ideas like this have floated around under the name of a "platform compatibility lint".
- wahern 4y agoBut whether implicitly or explicitly converted, the compiler would reject the code all the same, no? (Unless an explicit conversion would actually succeed, which is even worse. I'm not a Rust programmer so don't know. But this is why explicit casts are frowned upon in C.)
- JoshTriplett 4y agoThe proposal is that by default the compiler would refuse to do an infallible conversion from `u32` to `usize`, but if you declare up front "this code assumes at least a 32-bit target", then you gain the ability to do infallible conversions from u32 to usize. And if you declare up front "this code assumes at least a 64-bit target", you gain the ability to do infallible conversions from u64 to usize. Also, you can already convert u8 and u16 to usize infallibly, today. Only u32 and u64 currently require fallible conversions via try_from.
- __s 4y agoIt's more complaining that Rust is verbose when one wants to store small integers in structs but have intermediate computation use machine width integers Implicit widening doesn't tend to cause bugs. I don't think it'd be so bad if vec[u8] implicitly worked without needing vec[u8 as usize]
- estebank 4y agoFWIW, you can have a newtype wrapper around Vec that provides indexing access with any type you want. If you are porting or writing code that uses i32 for indices for some reason, then you use the wrapper instead of Vec with no runtime cost and no inline type conversions. This hasn't been solved at the language level yet because if we `impl Idx for u8` then inference in `vec[42]` breaks. Making inference support that in a principled manner will require a lot of work, and doing it in a hacky way might lock us into a suboptimal place that we haven't fully explored yet. I want to see some solution here at some point.
- colejohnson66 4y agoYou wouldn’t have to ‘impl Idx for u8’ if implicit upconversion was a thing. If it was, we could do this with no issues: let idx: u8 = 42; let val = vec[idx]; As GP said, the current “solution” is to do ‘idx as usize’ which is verbose.
- prirun 4y agoNim had this same problem a couple of years ago, and it's very annoying. I just checked some stdlib functions and it's still there: https://github.com/nim-lang/Nim/blob/devel/lib/std/varints.nim https://github.com/nim-lang/Nim/blob/devel/lib/std/varints.n... For example, in readVu64: 18: proc readVu64*(z: openArray[byte]; pResult: var uint64): int = 24: pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240 28: pResult = 2288u64 + 256u64*z[1].uint64 + z[2].uint64 31: pResult = (z[1].uint64 shl 16u64) + (z[2].uint64 shl 8u64) + z[3].uint64 33: let x = (z[1].uint64 shl 24) + (z[2].uint64 shl 16) + (z[3].uint64 shl 8) + z[4].uint64 I get that there has to be some indication to the compiler to distinguish between doing 8-bit unsigned math vs 64-bit unsigned math to evaluate these expressions. But having such a mish-mash in a stdlib function makes new users think, "There must have been a good reason to do it like this since it's in stdlib, but dang if I know what it is!" I ran into this myself a couple of years ago when looking at Nim and agree it is annoying, verbose, and ugly to be forced to specify types for obvious integer things.
- SemanticStrengh 4y agoThis is a big reason on why I loosed interest on rust
- deleted 4y ago[deleted]