Rust arithmetic overflow

cargo.toml:

[package]
name = "Learn"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]


[profile.dev]
overflow-checks=false
[profile.release]
overflow-checks=false

why doesn't rust panic in this case?:

println!("{}",225u8+255u8)

but rust panicking in that case:

let a:u8=255u8+255u8
1 Like

It doesn’t, this code shall fail to compile :face_with_monocle:

yes, but why isn't it compiled?

Because of default #[deny(arithmetic_overflow)]. You can allow or warn this:

#![allow(arithmetic_overflow)]

fn main() {
    let x = 255u8 + 255;
    println!("{x}"); // 254
}
1 Like

I know that , but I want to know why overflow-cheks doesn't work for arithmetic overflow

Overflow-checks only determines if there are runtime overflow checks. What happens at compile time is distinct. A compiler error is not the same as a runtime panic.

1 Like

thak you

but why didn't rust panic in the second case, it's due to the fact that println! is a macros?

Integer overflow panics when overflow-checks is on, but your Cargo.toml turned them off (and they're off by default in --release mode).

Note that the result of integer overflow is defined in Rust.

1 Like

I think the expectation is that the 255u8+255u8 would get evaluated at compile time, being a constant expression, and produce the same compilation error in both the println!and `let´ statements.

But it does not. Try it to see. Why the difference?

Note that it's an expression involving constant values, but that doesn't mean "always evaluated at compile-time".

const { 255_u8 + 255_u8 } will be a true constant expression, and it's in pFCP for stabilization: https://github.com/rust-lang/rust/pull/104087

I think in this case the compiler will optimise it

Yes, rustc and LLVM both can optimize it. But it's not observable whether they did or not.

I think if explicitly label it as a non-constant expression, the problem will be solved

if we use:

fn func (x:u8)->u8{
let b=x+255;
return b;
}

let  a:u8=func(255);

it is working

Hmm... OK, so that moves the question to why is one 225u8+255u8 evaluated at compile time but the other 225u8+255u8 not? They look exactly the same, so why not?

I think you're right, the compiler optimises it.and how to clearly mark it as a non-constant variable?

It's because of the way the macro is expanded.

println!("{}",225u8+255u8)

expands the argument as

&(225u8+255u8)

Interestingly you can do this and get the same behavior:

let x = &(225u8+255u8);

So that then begs the question of why is taking the reference runtime vs compile time?

Edit:
For clarity, 'runtime vs compile time' might be a bit misleading. In Debug mode, this is compile time checked just like any other arithmetic operation. In Release mode, or with

overflow-checks=false

it is not. This makes it the same as any other runtime evaluated expression.
So the question is more like "Why isn't this treated as a compile time constant expression?"

1 Like

Curiouser and curiouser...

1 Like

Maybe it's "rvalue static promotion".

Part of the reason we're adding const { … } is that the rules for that got super complicated. So we're winding them back as much as we can again, and asking people to request it when they actually want it using const { … } around the expression in question.