Skip to content

Instantly share code, notes, and snippets.

@hryx
Created July 13, 2019 23:41
Show Gist options
  • Save hryx/7fa554be42c7241c8b2392c3d6693b31 to your computer and use it in GitHub Desktop.
Save hryx/7fa554be42c7241c8b2392c3d6693b31 to your computer and use it in GitHub Desktop.
A somewhat misguided proposal

Overview

Today, casting between struct types is not supported. This is a proposal for supporting implicit casting from one struct type to another with the same number and types of fields. I'm not yet convinced that it's actually a good idea, but it seems worth discussing. I'm interested in hearing what other use cases and challenges people can think of.

This doesn't cover any explicit casting, e.g. something like @structCast(T, s).

Related: #685

Example

pub fn main() void {
    const S = struct {
        a: i32,
        b: f32,
    };

    const T = struct {
        a: i32,
        b: f32,
    };

    const s1: S = S{ .a = 4, .b = 2.0 }; // ok
    const s2: S = T{ .a = 4, .b = 2.0 }; // error: expected type 'S', found 'T'
    const s3: S = struct {
        a: i32 = 4,
        b: f32 = 2.0,
    }{}; // error: expected type 'S', found 's3'
}

S, T, and the anonymous struct type all have the same field types. In this simple example, it seems reasonable that implicit casting would occur.

Uses

Supplying int and float literals to anonymous struct literals

Once anonymous struct literals are supported, supplying int and float literal values to them would be somewhat problematic.

const S = struct {
    a: i32,
    b: f32,
};

fn f(s: S) void {}

pub fn main() void {
    f(.{.a = 6, .b = 6.6});
}

The effective type of the anonymous struct literal passed to f above is:

struct {
    a: comptime_int,
    b: comptime_float,
}

Challenges

Layout

The layout of normal structs is not defined. If struct types S and T have identical definitions but the compiler ordered or padded the fields differently, would this make casting between them infeasible?

Furthermore, should extern or packed structs support casting in any circumstance?

Namespaced constants and methods

Let's re-imagine our example. Is it possible to cast the following types to each other?

const S = struct {
    a: i32,
    b: f32,
};

const T = struct {
    a: i32,
    b: f32,

    pub fn incr(self: *@This()) void {
        self.a += 1;
    }
};

const U = struct {
    a: i32,
    b: f32,

    pub const rank = "despot";
};

Recursive implicit casting

Structs can contain structs, which can contain structs, ad nauseam. Supporting casting between S and T here might reveal some complexity, either semantically or in the compiler implementation.

const S = struct {
    a: i32,
    sub: G,
};

const T = struct {
    a: i32,
    sub: H,
};

const G = struct {
    ok: bool,
};

const H = struct {
    ok: bool,
};

Miscellaneous

  • Do field names need to be equal?
  • Besides comptime_X types, will this work for enum literal values in fields?
  • Pointers: Given equivalent struct types S and T, can one cast *S to *T?

Alternatives

A workaround for the comptime_int issue described above is to use explicit casting on the fields. This is fairly painless now, but would be less so after #1757 . Comparing all three:

pub fn main() void {
    f(.{.a = 6, .b = 6.6});
    f(.{.a = i32(6), .b = f32(6.6)});
    f(.{.a = @cast(i32, 6), .b = @cast(f32, 6.6)});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment