-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: "embed" keyword. #9911
Comments
Whats the difference from status quo other than needing to not name the substructs field and use it to access its fields? test "syntax" {
const SubStruct = struct { v: usize };
const Struct = struct {
sub: SubStruct,
fn useSubStruct(self: *@This()) void {
self.sub.v += 1;
}
};
var s: Struct = .{ .sub = .{ .v = 123 } };
s.useSubStruct();
} Data layout const Vec3DataLayout = enum { xyz, zyx };
fn Vec3Data(comptime T: type, layout: Vec3DataLayout) type {
return switch (layout) {
.xyz => extern struct {
x: T,
y: T,
z: T,
const DataT = T;
},
.zyx => extern struct {
z: T,
y: T,
x: T,
const DataT = T;
},
};
}
test "use case[0]: data layouts" {
const impl = Vec3Data(f32, .xyz);
const Vec3 = struct {
data: impl,
fn scale(vec: *const @This(), scalar: impl.DataT) @This() {
return .{
.data = .{
.x = vec.data.x * scalar,
.y = vec.data.y * scalar,
.z = vec.data.z * scalar,
},
};
}
};
const a: Vec3 = .{ .data = .{ .x = 1, .y = 1, .z = 1 } };
const b = a.scale(10);
try std.testing.expectEqual(@as(impl.DataT, 10), b.data.x);
}
Inheritance const Sprite = struct {
texture: [][]const Pixel,
rect: Rect,
fn drawSprite(self: *@This()) void {
draw(self.texture, self.rect);
}
};
const AnimatedSprite = struct {
sprite: Sprite,
frames: []Rect,
current_frame: usize,
fn advanceFrame(self: *@This()) void {
self.current_frame = @mod(self.current_frame + 1, self.frames.len);
self.sprite.rect = self.frames[self.current_frame];
}
};
test "use case[1]: \"inheritance\"" {
const frames = calculateFrames();
var animated_sprite: AnimatedSprite = .{
.sprite = .{
.texture = loadTexture("texture.png"),
.rect = frames[0],
},
.frames = frames,
.current_frame = 0,
};
while (true) {
animated_sprite.sprite.drawSprite();
animated_sprite.advanceFrame();
}
} |
From a purely functional point of view, there's no difference besides that. It's a usability feature, and an expansion of |
Another proposal with similar ideas is #7969, which approaches it with a kind of abstract struct concept - partial structs. |
You can already implement this without any language-level feature. |
Motivation
While (recreationally) trying to write a linear algebra library, i encountered a problem, where i was forced to repeat a lot of code, if i want to leave some
struct
s' layout for user to decide (to mantain compatibility with multiple rendering APIs).So here's possible solution!
embed
keyword would be used to acces fields of a fieldstruct
through it's container.Besides just data layout of a struct, this allows to cover most of OOP's strengths, without any of the drawbacks.
Syntax
Might be useful to allow naming embedded structs:
Also might be useful to be able to provide default values:
Advantages
Data layouts
This example is pretty silly, but that type of pattern becomes infinetely more useful with matrices and quaternions.
"Inheritance"
Embedding a struct can be used to extend a struct behaviour, without the mess of virtual methods.
"Interfaces"
This use case only serves making sure that fields are present and of a specified type. One can guarantee it with testing, but this is much more self-documenting.
Theres no way for a
struct
to depend on itself, so trying to fork member functions based on a container type isn't really possible, so this doesn't break "no hidden control flow" rule.I feel like with this keyword, it is absolutely necessary to ensure that embedded
struct
can be used by itself. So, not depending on it's container at all, BUT:To be useful, embedded
struct
needs to reinterpret it's@This()
in a context of a container.The text was updated successfully, but these errors were encountered: