Can casting in safe Rust ever lead to a runtime error?

我是研究僧i 提交于 2021-02-10 04:13:50

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!