#Syntax for structs and errors

1 messages · Page 1 of 1 (latest)

vernal torrent
#

I'm trying to understand the syntax for declaring structs and errors.
const SomeStruct = struct { ... }
Is this an assignment? If, so, is struct an expression?

Also, I read in the docs, "all structs are anonymous".

I would expect something closer to C's syntax that declares a named struct, struct SomeStruct { ... } or Java's syntax that declares a named class, class SomeClass { ... }.

Especially because Zig functions are declared as named functions, fn foo( ... ) void { ... }

warm sinew
#

it's an expression that generates a type, and you can use it the same way elsewhere. However, note that this code doesnt compile:

const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

fn move_right(p: struct { x: i32, y: i32 }) Point {
    return .{ .x = p.x + 1, .y = p.y };
}

fn move_left(p: Point) struct { x: i32, y: i32 } {
    return .{ .x = p.x - 1, .y = p.y };
}

test {
    var player = Point{ .x = 0, .y = 0 };
    player = move_right(player);
    player = move_right(player);
    player = move_left(player);
    try std.testing.expectEqual(struct { .x = 1, .y = 0 }, player);
}

due to:

error: expected type 'z168.move_right__struct_968', found 'z168.Point'
#

https://ziglang.org/documentation/0.13.0/#Struct-Naming
note, after it says that all structs are anonymous, it adds

  • If the struct is in the initialization expression of a variable, it gets named after that variable.
  • If the struct is in the return expression, it gets named after the function it is returning from, with the parameter values serialized.
  • Otherwise, the struct gets a name such as (filename.funcname.__struct_ID).
  • If the struct is declared inside another struct, it gets named after both the parent struct and the name inferred by the previous rules, separated by a dot.
#

so my code doesn't work even though all of the structs are 'identical' because Point isn't the same as the various other inferred names.

#

the inconsistency between how structs are declared and how functions are declared has been brought up in github issues.

#

AFAIK the inconsistency's going to remain.

#

if it makes you feel better, const std = @import("std") is also adding a struct to your program which can have members.

vernal torrent
#

Thanks!

warm sinew
#

point.zig:

pub const Point = @This();
x: i32,
y: i32,

and using it:

const std = @import("std");
const Point = @import("point.zig").Point;

fn move_right(p: *Point) void {
    p.x += 1;
}

fn move_left(p: *Point) void {
    p.x -= 1;
}

test {
    var player: Point = .{ .x = 0, .y = 0 };
    var monster: Point = .{ .x = 0, .y = 0 };
    for (0..2) |_| move_right(&player);
    for (0..5) |_| move_right(&monster);
    try std.testing.expectEqual(2, player.x);
    try std.testing.expectEqual(5, monster.x);
}
#

maybe more illustrative, you can see that point.zig also has functions in the namespace that aren't 'part' of the Point struct:

pub const Point = @This();
x: i32,
y: i32,

pub fn move_right(p: *Point) void {
    p.x += 1;
}

pub fn move_left(p: *Point) void {
    p.x -= 1;
}

used:

const std = @import("std");
const nav = @import("point.zig");
const Point = nav.Point;

test {
    var player: Point = .{ .x = 0, .y = 0 };
    var monster: Point = .{ .x = 0, .y = 0 };
    for (0..2) |_| nav.move_right(&player);
    for (0..5) |_| nav.move_right(&monster);
    try std.testing.expectEqual(2, player.x);
    try std.testing.expectEqual(5, monster.x);
}
#

grep -rP '^\w+:.*,$' to find examples of this in std

#

ah that's wrong ofc, monster.move_right() works fine.

vernal torrent
#

I see an interesting example in std.heap.HeapAllocator, that makes it clearer why the choice for expressions.

const HeapAllocator = switch(builtin.os.tag) { .windows => struct { ... } else => @compilerError("Unsupported OS") }

The assignment syntax makes it easier to handle compilation for different targets. I can see now a nice use case for the syntax.

However, that's 1% or less of use cases, and I still think that the named struct syntax could exist for the 99% common use case.

warm sinew
cosmic stag
#
fn foo(params: struct {
    fizz: usize,
    buzz: bool,
}) void {
    _ = params;
}
#

(basically allows for easy named parameters)

#

Also there is no reality in which there are two ways to declare the same thing