Ok, I’d like to hash this out a little more.
@floooh suggested the example of “a whole array of structs” being alloc’d and initialized (with some data) simultaneously, which, to me, seems ludicrous indeed; I agree. Your comment, “… when you’re used to it… you will probably do the same with not-so-simple cases, and that is where the harm is” - I want to respect, but it doesn’t “feel” like I would. So, if I have a “simple” Zoo, and it has members that really don’t want default values, e.g., because data invariants of the struct could be violated by omitting a field during initialization (see here), you might provide:
const Zoo = struct {
a: u8,
b: u8,
const default: Zoo = .{
.a = 3,
.b = 4,
};
fn init(a: u8, b: u8) Zoo {
return Zoo {
.a = a,
.b = b,
};
}
};
Now, if you were creating a zoo with an allocator, and create() should really “do no initialization”, it would perhaps at least(?):
...
fn create(al: Allocator) !*Zoo {
const r = try al.create(Zoo);
r.* = default; //?
return r;
}
But would it be the most conventional? to require the caller to:
var myzoo = try Zoo.create(al);
myzoo.* = { .a = 1, .b = 2 };
Or would it be “unconventional”? (inappropriate by convention?) to provide:
...
fn make(al: Allocator, a: u8, b: u8) !*Zoo {
const r = try al.create(Zoo);
r.* = .{ .a = a, .b = b };
return r;
}
So that the user can one-liner instead:
var myzoo = try Zoo.make(al, 1, 2);
And, finally, if such a “default” (as provided above) didn’t really make sense, and neither does an “uninitialized Zoo”, then would it be appropriate to ONLY provide that make() function? If so, would it be better to call it something like make(), indeed, instead of create(), in order to avoid conveying that it was a standard create() that did NO initialization? (Though, of course, the (…, a, b) parameters would make it pretty clear that it was going to initialize values.)
I don’t mean to nit-pick, but I’d like to develop routines that make sense and coincide with conventions the community has evolved… and I still haven’t spent enough time in various peoples’ libraries to feel confident drawing zeitgeist conclusions of my own (especially as the language and std are still evolving themselves and I do see a lot of “not so right-looking” code in the wild, too).
(Sorry, I usually compile code before posting, like this, but I’m growing confident enough to try freehanding now, so, forgive any flubs.)
EDIT: of course, for such a simple struct, Zoo wouldn’t really need a create() fn; caller could just al.create(Zoo); first by themselves, but for a more interesting struct, of course, providing that creation helper makes sense, even if it’s ONLY doing allocation. By this token, a make() function would perhaps first call sister create(), then do the initialization (perhaps calling sister init(), such that make() might MERELY be a convenience r = create(); r.* = init();… but I didn’t think of packaging it that way when I wrote the above. The bottom-line question still remains: is this make() idea an anti-pattern that should be avoided? Is it “fine”, even to do this in a function named create(), with args for initialization, and maybe even ONLY provide such a create(al, args…) if, in your application, it makes no sense to allocate-create an uninitialized version of the object? Or is it best to make sure, regardless, that create() only allocs, and never initializes with provided values (at the very least, so that callers don’t have to worry about whether expensive stuff is being done inside of create())? Sorry that’s so many questions wrapped in one, but I’ll be very satisfied to have the guidance.