I am learning Zig and programming by crafting an http server on CodeCrafters and trying to practice structuring my code using Zig’s different constructs. In this example, ServerConfig has default values and is being passed as an argument in the init method. Will the default values of ServerConfig struct be processed at compile time (with types for each field then be: .ip: [9:0]const u8 and .buffer_size .max_connections and .port as comptime_int)?
/// ServerConfig holds all configuration parameters for the HTTP server
const ServerConfig = struct {
ip: []const u8 = "127.0.0.1",
buffer_size: usize = 4096,
max_connections: u32 = 1000,
port: u16 = 4221,
};
/// ServerContext maintains the state and resources for the HTTP server
const ServerContext = struct {
config: ServerConfig,
allocator: mem.Allocator,
listener: net_modified.Server,
active_connections: std.atomic.Value(u32),
/// Initialize a new ServerContext with the given configuration
pub fn init(allocator: mem.Allocator, config: ServerConfig) !ServerContext {
const address = try net_modified.Address.resolveIp(config.ip, config.port);
// Enable address reuse to prevent "address already in use" errors
const listener = try address.listen(.{ .reuse_address = true });
return ServerContext{
.config = config,
.allocator = allocator,
.listener = listener,
.active_connections = std.atomic.Value(u32).init(0),
};
}
/// Clean up server resources
pub fn deinit(self: *ServerContext) void {
self.listener.deinit();
}
};
What about if instead of having default values in this struct, I instantiate the struct into a variable with defined values like this:
Both ServerConfig.init and ServerConfig{} will produce a comptime known value.
However all function calls are by default runtime. So the resultion object will be passed at runtime to the init function.
If you want your arguments to be passed at compile-time, you would make the argument comptime config: ServerConfig or call the function in a comptime scope.
However in practice this likely won’t matter at all, since llvm will just inline and optimize the function anyways.
I welcome, I think all of them works, I prefer the public const declaration, combined with default values, I think it’s nice to be explicit. I think you just need to be careful, when you allocate you don’t get the default values, unless you assign them
I find this really good, thank you…I saved your example so I can refer to it later. I think your way of setting up a server seems better and more straightforward than how I did it but because my goal is to become functional with zig on my free time and there is so much to learn (zig, programming concepts like pointers, memory allocations, system calls, data structures, …) I am trying to focus my attention to optimize my progress for the time I can dedicate to it so I’ll mainly try and use the standard library for everything else…I used the net library to create the server even though your way seems more practical. This is how I ended up setting comptime on server configs, I chose that way because it makes it obvious in main() that I am using default configs:
Use default field values if each such value makes independent sense, i.e. it is still valid when every other is overridden by the user.
Use default or init constants if that is not the case, i.e. the default values are linked.
More details on #2
The need to have default/init constant shouldn’t occur often, because it implies some combination of valid field values results in a semantically invalid struct. It usually only happens when fields require initialization (usually allocation) using external data; this is why init() method is much more common than init constant.
In your example, ServerConfig is just a grab bag of independent fields; if there is a reasonable default value for each one then there is no reason not to provide it as the field default.
This also makes the init/default constant obsolete since, if every field has a default value, it just becomes a synonym to .{}.
My goal was to learn an effective way to use comptime when values are known at compile time. I created ServerConfig to group variables required by the methods I need from the net struct in the standard library. It was more of a practice exercise to learn comptime and I know its not practical as methods in std/net.zig are not comptime. My understanding is that adding default values to each field would not have made it possible to initialize a listener during compile time (if related function in net was also comptime). Let me know if my understanding is incorrect, thanks!