Are casts encouraged in Rust?
Are casts encouraged in Rust?
Posted Jun 30, 2025 22:56 UTC (Mon) by alx.manpages (subscriber, #145117)In reply to: Are casts encouraged in Rust? by NYKevin
Parent article: How to write Rust in the kernel: part 2
This is an interesting discussion. You have a point, which I also made with someone else recently.
Your right in calling C's implicit conversions messy, but it's not true of all of them.
C has three types of implicit conversions:
- Integer promotions.
These trigger for any fundamental type narrower than an int. I've called them a "cancer" myself recently. They are there because of historical reasons. I would remove them from the language if I could, but of course we can't at this time.
It is bad that a uint16_t is promoted to an int on almost every situation, which even changes its signedness.
The good thing is that few people actually use narrow integers like short, int16_t, or uint8_t.
The better thing is that the new _BitInt(N) integers added in C23 don't have integer promotions: a _BitInt(16) will not be promoted to an int.
So, I'd say we've partially solved this issue in C. Although we're not over. We also need to be able to specify literals of such types. I've written a proposal for the C Committee (and an extension request to both GCC and Clang) for that:
<https://github.com/llvm/llvm-project/issues/129256>
- Usual arithmetic conversions.
When adding, comparing or otherwise using two different types of integers in an operator that takes two operands, these trigger.
So, if you have
int a = 42;
long b = 7;
if (a < b)
return a;
you'll get the usual arithmetic conversions to turn that int into a long. Since both retain the original signedness, this conversion is harmless, and doesn't trigger any diagnostics at all. This is a good conversion.
If you had that comparison between integers of different signedness, you could get a warning with -Wsign-compare or -Wsign-conversion (depending if you're comparing them or adding/multiplying/... them, but they're both essentially the same thing).
alx@debian:~/tmp$ cat c.c
int
main(void)
{
int a = 42;
unsigned long b = 7;
if (a < b)
return 0;
}
alx@debian:~/tmp$ clang -Weverything c.c
c.c:7:8: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
7 | if (a < b)
| ~ ^ ~
1 warning generated.
and
alx@debian:~/tmp$ cat c.c
int
main(void)
{
unsigned int a = 42;
int b = 7;
if (a < b)
return 0;
}
alx@debian:~/tmp$ clang -Weverything c.c
c.c:7:8: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare]
7 | if (a < b)
| ~ ^ ~
1 warning generated.
This is sadly not turned on on -Wall -Wextra, but this is one diagnostic that you'd usually want, and most of the times it uncovers subtle bugs.
I said "could", because that diagnostic is not always triggered. It triggers if there can be information loss. There's a case where there can't be information loss: the unsigned integer is turned into a wider signed integer type that can represent all of the values that the unsigned integer can hold:
alx@debian:~/tmp$ cat c.c
int
main(void)
{
unsigned int a = 42;
long b = 7;
if (a < b)
return 0;
}
alx@debian:~/tmp$ clang -Weverything c.c
alx@debian:~/tmp$
This is another good conversion you want to happen. It's good that we don't diagnose it. - And then there are implicit conversions as if by assignment.
The C standard describes all implicit conversions as if by simple assignment. These happen, for example, when you assign some integer to a variable of another integer type.
This can be a narrowing conversion, in which case you'll get a very explicit diagnostic:
alx@debian:~/tmp$ cat c.c
int
main(void)
{
long l = 42;
int i = l;
}
alx@debian:~/tmp$ clang -Weverything -Wno-unused c.c
c.c:5:10: warning: implicit conversion loses integer precision: 'long' to 'int' [-Wshorten-64-to-32]
5 | int i = l;
| ~ ^
1 warning generated.
Again, this is not in -Wall -Wextra, but you probably want to turn on -Wshorten-64-to-32 (and similar ones) for your projects, and disable it only when you know those conversions are good.
I personally disable it selectively in a few places with
#pragma clang diagnostic ignored "-Wshorten-64-to-32"in a few places in a library where I know that's exactly what I want.
It can also be a sign-changing conversion:
alx@debian:~/tmp$ cat c.c
int
main(void)
{
unsigned int l = 42;
int i = l;
}
alx@debian:~/tmp$ clang -Weverything -Wno-unused c.c
c.c:5:10: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversion]
5 | int i = l;
| ~ ^
1 warning generated.
which is covered by the same -Wsign-conversion I mentioned earlier, which you also want on all the time, with a few exceptions maybe.
---
So, the -Wall -Wextra compiler diagnostics are a bit lacking, but if you turn on all available diagnostics, they're quite safe. Rust's .into() seems like C's behavior when the diagnostics are on, except that it doesn't allow the few conversions that don't produce any diagnostic in C, and which are actually Good Conversions. Also, Rust's .into() is just typographic noise, because good conversions is what I want all the time.
---
Then there's the issue that Rust is unable to do .into() with constant expressions, which forces you to use casts. That's worse than not allowing the good conversions without noise; this is plain dangerous.
