Zig: make aggregate types non-copyable by default; provide copyable attribute

Created on 29 Nov 2019  路  4Comments  路  Source: ziglang/zig

This is a competing proposal with #3803. It solves the same problem in a different way, which is arguably simpler, and safer by default.

It's pretty easy to explain: make all aggregate types non-copyable by default. So this would be an error:

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

test "copy a point" {
    var pt = Point{.x = 1, .y = 2};
    var pt2 = pt; // error: copy of struct which does not have the `copyok` attribute
}

But this would work:

const Point2 = struct copyok {
    x: i32,
    y: i32,
};

test "copy a point" {
    var pt = Point{.x = 1, .y = 2};
    var pt2 = pt; // OK
}

Some notes:

  • Enums, anonymous struct literals and anonymous list literals would be copyable
  • Non-copyable types would be always passed as a reference when the parameter used pass-by-value parameter syntax
  • This would probably be too inconvenient without #2761 and #2765 implemented.
proposal

Most helpful comment

isn't this basically the same thing as #3803 but with pinned types rather than fields? perhaps these can be unified by having a @Pin attribute that marks types as immovable.
this proposal becomes:

const Point2 = @Pin(struct {
    x: i32,
    y: i32,
});

and #3803 becomes:

const FixedBufferAllocator = struct {
    allocator: @Pin(Allocator),
    buf: []u8,
};

in cases that it doesn't make sense for the type to be movable such as function pointers @Pin would be a compiler error.

All 4 comments

* Enums, anonymous struct literals and anonymous list literals would be copyable

I don't think enums should be, what about something like:

const Foo = struct {
    const Self = @This();

    x: i32,
    qux: enum {
       a, b, c,
       pub fn validate(thisenum: *@This()) bool {
           const self = @fieldParentPtr(Self, "qux", thisenum);
           return switch (thisenum) {
               a => self.x < 42,
               b => self.x < 100,
               c => self.x >= 50,
           };
        }
    },
};

As good as it sounds, this proposal implies explicit move- and copy-semantics for a lot of types and special care for a lot of generics:

https://github.com/ziglang/zig/blob/85e1e3b95f1f1699a842a5e889d8987692a829a4/lib/std/array_list.zig#L139-L142

This would not work with non-copyok types, so most of std containers would have to be implemented again with special care for non-copyable types.

And as @daurnimator said: @fieldParentPtr gives us freedom to use any type for "inheritance", so any type should be markable as non-copyable

isn't this basically the same thing as #3803 but with pinned types rather than fields? perhaps these can be unified by having a @Pin attribute that marks types as immovable.
this proposal becomes:

const Point2 = @Pin(struct {
    x: i32,
    y: i32,
});

and #3803 becomes:

const FixedBufferAllocator = struct {
    allocator: @Pin(Allocator),
    buf: []u8,
};

in cases that it doesn't make sense for the type to be movable such as function pointers @Pin would be a compiler error.

Perhaps there should be a special copy operator := for aggregate types to make the intent clear?

Was this page helpful?
0 / 5 - 0 ratings