Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I provide a group of related types with a single type parameter in Rust?

I'm developing a library that works with PE files for both 32- and 64-bit architectures. Some structures will reference both virtual addresses (VA, e.g. ImageBase) and relative virtual addresses (RVA, e.g. section offset), for example:

type VA32 = u32;
type RVA32 = i32;

struct Header32 {
    image_base: VA32,
    section_offsets: Vec<RVA32>,
}

let hdr = Header32 { /* ... */ };

When dealing with 32-bit PE files, a VA should be 32-bits and unsigned, and an RVA should be 32-bits and signed. For 64-bit PE files, both types should be 64-bits.

I'd like my structs to use the appropriate width for these types, probably by making them generic:

struct Header<VA, RVA> {
    image_base: VA,
    section_offsets: Vec<RVA>,
}

type VA32 = u32;
type RVA32 = i32;

let hdr: Header<VA32, RVA32> = Header { /* ... */ };

But VA32 only ever goes with RVA32, and VA64 should only ever be provided with RVA64. Is there an idiomatic way that I can express this?

Using a completely made up syntax, I desire to do something like:

struct Header<Arch> {
    image_base: arch.VA,
    section_offsets: Vec<arch.RVA>,
}

type Arch32 = { VA: u32, RVA: i32 }

let hdr: Header<Arch32> = Header { /* ... */ };
like image 450
Willi Ballenthin Avatar asked Jan 26 '23 10:01

Willi Ballenthin


1 Answers

You can do something similar to your made up syntax using traits with associated types and zero sized structs as markers.

trait Arch {
    type VA;
    type RVA;
}

#[derive(Debug)]
struct Arch32;
impl Arch for Arch32 {
    type VA = u32;
    type RVA = i32;
}

#[derive(Debug)]
struct Arch64;
impl Arch for Arch64 {
    type VA = u64;
    type RVA = i64;
}

#[derive(Debug)]
struct Header<T: Arch> {
    image_base: T::VA,
    section_offsets: Vec<T::RVA>,
}

playground

like image 66
Pavel Arnold Avatar answered Jan 30 '23 08:01

Pavel Arnold