DEV Community

Cover image for Type system in Rust - subtypes without inheritance? pwned?
ProgramCrafter
ProgramCrafter

Posted on

Type system in Rust - subtypes without inheritance? pwned?

Are there features in Rust type system beyond runtime-determined types of trait objects and ability to transmute them into others? Maybe some types have subtyping relation, which allows to safely cast them to super-s?

A fair warning: we're delving into The Rustonomicon, called also The Dark Arts of Unsafe Rust.

THE KNOWLEDGE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF UNLEASHING INDESCRIBABLE HORRORS THAT SHATTER YOUR PSYCHE AND SET YOUR MIND ADRIFT IN THE UNKNOWABLY INFINITE COSMOS.

Also, we will be assuming considerable prior knowledge, mostly on topic of references (wide and thin) and their lifetimes.

🦀 There are most definitely sub- and supertypes.

Cases of subtyping in Rust

trait Vehicle {}
trait Car : Vehicle {}

struct Chevle;
impl Vehicle for Chevle {}
impl Car for Chevle {}
Enter fullscreen mode Exit fullscreen mode

Trait objects

  1. &Chevle (read-only reference) may be casted either to &dyn Car or to &dyn Vehicle. After all, if we have read access to our car, then it's also access to an unspecified kind of car or a vehicle.

    Why mutable references can't be casted so?
    Let's suppose we have garage-variable that holds Chevle. If we were able to create &mut dyn Car reference to it, we'd be able to put Kia there, and after ref lifetime expiry Kia would stand in garage for Chevle!

  2. &dyn Car to &dyn Vehicle, according to the same rationale (planned, since that requires changing vtables in wide pointers).

Lifetimes

fn bar<'a>() -> &'a str {
    let s: &'static str = "dev.to";
    s
}
Enter fullscreen mode Exit fullscreen mode
  1. References have lifetimes; a reference may have its lifetime shortened.

    Why mutable references can change here?
    I will demonstrate this with a sample code.
    fn foo() {
      /* 'x */ {
        let mut i = 1;
        let p: &mut usize = &mut i;  // &'x mut usize
        /* 'y */ {
          let mut j = 4;
          p = &mut j;    // cannot assign twice to immutable variable `p`
        }
        *p += 1;
      }
    }
    fn bar() {
      /* 'x */ {
        let mut i = 1;
        let mut p: &mut usize = &mut i;  // &'x mut usize
        /* 'y */ {
          let mut j = 4;
          p = &mut j;
        }  // `j` does not live long enough, since its borrow `p` is used below 
        *p += 1;
      }
    }
    

Generic types

That is a complex subject perhaps best explained by its page in The Rustonomicon.

  • F is covariant over T if T being a subtype of U implies that F is a subtype of F (subtyping "passes through")
  • F is contravariant over T if T being a subtype of U implies that F is a subtype of F
  • F is invariant over T otherwise (no subtyping relation can be derived)

A Rust safety bug

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
    let f: fn(_, &'a T) -> &'static T = foo;
    f(UNIT, x)
}
Enter fullscreen mode Exit fullscreen mode

There's a code that promotes reference having any lifetime to 'static - one outliving the program, thus valid for its entire run - without memory allocation. You may notice that there's an ignored parameter creating unchecked assertion that 'b outlives 'a, which is the root of this issue. There are quite a few variants how to do this, so please remain aware (or don't use double references)!

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay