Not so Trivial Casts

By Nikita Bishonen1 minute read



Not so Trivial Casts

There is an interestion lint in Rust called trivial_casts, yet it turns out to be not so trivial.

It may be possible that this will become a warning in the future

so let’s explore some code examples where I will compare:

  1. Trivial casts
  2. Variables types definitions
  3. Implicit coercion

I will not add any comments, except compiler outputs, so you will be able to see difference by yourself and decide when and how to use types casting in Rust code. I will only say that trivial_cast_supertype_with_shadowing seems to be a good example on how trivial_casts can become not so trivial even in small example (imagine what it can do in a bigger chain of calls and transformations/casts).

#![allow(dead_code)]
#![warn(trivial_casts)]

trait Polygon {}
trait TwoSidedPolygon: Polygon {}

struct Square([u32; 8]);
impl Polygon for Square {}
impl TwoSidedPolygon for Square {}

struct Triangle([u32; 6]);
impl Polygon for Triangle {}

#[cfg(test)]
mod tests {
    use crate::{Polygon, Square, Triangle, TwoSidedPolygon};

    #[test]
    fn explicit_definition_supertype() {
        let square = Square([2; 8]);
        let square: &dyn Polygon = □
        process(square);
        // process2(square); the size for values of type `dyn Polygon` cannot be known at compilation time
        // process3(square); the trait bound `dyn Polygon: TwoSidedPolygon` is not satisfied
        // process_square(square); expected &Square, found &(dyn Polygon + 'static)
    }

    #[test]
    fn explicit_definition_subtype() {
        let square = Square([2; 8]);
        let square: &dyn TwoSidedPolygon = □
        process(square);
        // process2(square); the size for values of type `dyn Polygon` cannot be known at compilation time
        process3(square);
        // process_square(square); expected &Square, found &(dyn TwoSidedPolygon + 'static)
    }

    #[test]
    fn trivial_cast_supertype() {
        let square = Square([2; 8]);
        let square = &square as &dyn Polygon; // trivial cast: `&Square` as `&dyn Polygon`
        process(square);
        // process2(square); the size for values of type `dyn Polygon` cannot be known at compilation time
        // process3(square); the trait bound `dyn Polygon: TwoSidedPolygon` is not satisfied
        // process_square(square); expected &Square, found &dyn Polygon
    }

    #[test]
    fn trivial_cast_subtype() {
        let square = Square([2; 8]);
        let square = &square as &dyn TwoSidedPolygon; // trivial cast: `&Square` as `&dyn TwoSidedPolygon` 
        process(square);
        // process2(square); the size for values of type `dyn Polygon` cannot be known at compilation time
        process3(square);
        // process_square(square); expected &Square, found &(dyn TwoSidedPolygon + 'static)
    }

    #[test]
    fn trivial_cast_struct_type() {
        let square = Square([2; 8]);
        let square = &square as □ // trivial cast: `&Square` as `&Square`
        process(square);
        process2(square);
        process3(square);
        process_square(square);
    }

    #[test]
    fn auto_coercion() {
        let square = Square([2; 8]);
        let polygon = □
        process(polygon);
        process2(polygon);
        process3(polygon);
        process_square(polygon);
    }

    #[test]
    fn shadowed_auto_coercion() {
        let square = Square([2; 8]);
        let square = Triangle([3; 6]);
        let square = □
        process(square);
        process2(square);
        // process3(square); the trait bound `Triangle: TwoSidedPolygon` is not satisfied
        // process_square(square); expected &Square, found &Triangle
        process_triangle(square);
    }

    #[test]
    fn trivial_cast_supertype_with_shadowing() {
        let square = Square([2; 8]);
        let square = Triangle([3; 6]);
        let square = &square as &dyn Polygon; // trivial cast: `&Square` as `&Square`
        process(square);
        // process2(square); the size for values of type `dyn Polygon` cannot be known at compilation time
        // process3(square); the trait bound `dyn Polygon: TwoSidedPolygon` is not satisfied
        // process_square(square);  expected &Square, found &dyn Polygon
    }

    fn process_square(_square: &Square) {}
    fn process_triangle(_triangle: &Triangle) {}
    fn process(_polygon: &(impl Polygon + ?Sized)) {}
    fn process2(_polygon: &impl Polygon) {}
    fn process3(_polygon: &(impl TwoSidedPolygon + ?Sized)) {}
}

Comments

You can comment on this blog post by publicly replying to this post using a Mastodon or other ActivityPub/Fediverse account. Known non-private replies are displayed below.

Open Post