问题
I'm fiddling a bit with Any
and casting just to get a deeper understanding of Rust. From C# I'm used to the fact that casting can lead to runtime exceptions because casting in C# basically means Dear compiler, trust me, I know what I'm doing please cast this into an int32
because I know it will work.
However, if you're doing an invalid cast the program will blow up with an Exception at runtime. So I was wondering if casting in (safe) Rust can equally lead to a runtime exception.
So, I came up with this code to try it out.
use std::any::Any;
fn main() {
let some_int = 4;
let some_str = "foo";
{
let mut v = Vec::<&Any>::new();
v.push(&some_int);
v.push(&some_str);
// this gives a None
let x = v[0].downcast_ref::<String>();
println!("foo {:?}", x);
//this gives Some(4)
let y = v[0].downcast_ref::<i32>();
println!("foo {:?}", y);
//the compiler doesn't let me do this cast (which would lead to a runtime error)
//let z = v[1] as i32;
}
}
My observation so far is that the compiler seems to prevent me from this kind of runtime exceptions because I have to cast through downcast_ref
which returns an Option
which makes it safe again. Sure, I can unwrap
on a None
to blow it up but that's not my point ;)
The compiler prevents me from writing let z = v[1] as i32;
which could lead to a runtime error. So, is it correct to assume that casting in safe Rust will never result in a runtime error?
I know that preventing runtime errors is exactly what Rust is all about so it makes perfect sense, I just want to validate my observation :)
回答1:
Casting with as
in Rust is very limited. It only allows casting between primitive numeric and character types, between pointers and references and for creating trait objects out of values of concrete types, and that's all - as
is not overloadable, for example. Therefore, casting with as
is always panic-free, though you can observe numeric overflows if you're casting a value which can't be represented in the target type, which may or may not be desirable.
In Rust there is no such thing as the cast operator from C# or Java. The closest thing to it would be std::mem::transmute()
which is a lot like reinterpret_cast
from C++. It is unsafe
, however, and even it has its limitations - it can only transform values of types having the same size.
回答2:
Well, that depends on how you define "runtime error" and "result in".
As Vladimir said as
is just for primitive conversions that can't really fail. But there is currently (Rust 1.3) an evil little hole in this: casting floating point values to an integer.
If you try to cast a floating point value that doesn't have a corresponding integer value, the result is that you end up with an integer containing "something". A weird "something" that LLVM assumes cannot exist (because of course you checked that the conversion made sense before you did it). And compilers optimise based on things that can't happen.
The net result is that you can crash or corrupt memory by using really big values to create weird undefined integers that produce inconsistent results at runtime, which might include both controlled and uncontrolled crashes.
I mean, it's not supposed to do that, and the solution is probably going to involve making as
panic, because what else do you do when someone asks the compiler to evaluate f32::NAN as i32
?
来源:https://stackoverflow.com/questions/32854394/can-casting-in-safe-rust-ever-lead-to-a-runtime-error