Builder/Command pattern

We all want readable code. I struggle to sometimes understand function calls if i don’t know the names and descriptions of the function args. Thus, I already adopted quasi “named parameters” for almost all my functions by using struct args. However, due to the current version of ZLS not autocompleting my args when i write doSomething(.{arg1=1}), I am considering using the builder pattern, i.e. (doSomething{.arg1=1}).call(). I can’t see any reason why the compiler won’t generate very similar machine code. Another benefit of this approach is when passing functions with their args to other functions (e.g. when benchmarking), that you get nice LSP linting for building the command and you simply provide .{} as the ArgsTuple.

Any major advice against this, except that its not “the standard way” of calling functions?

I don’t know your setup, but some editors leverage ZLS to show function arguments name.

Struct args isn’t bad, but I don’t remember it not completing.

I use nvim. I assume this is then some sort of ghost-text thats inserted by the ide? Good to know there are options out there, but tbh I’m not the biggest fan of ghost-text and also i want to hover on a field to get its description; but thanks for mentioning.

On the completion:
var user: User = .{.name=”Daniel”} autocompletes ‘.name’
var user: User = .init(.{.name=”Daniel”} does not autocomplete ‘.name’, but does show linting afterwards

This may not be a good motivation, after all, this motivation is just a temporary workaround under the imperfect conditions of the third-party LSP. IFIR, zls can achieve completion in this situation. If possible, we hope there is only one clear way of doing things.

And, note #5038. Needs to use something with more noise like (@as(DoSomething, {.arg1=1})).call()

However, I think that at least (@as(DoSomething, {.arg1=1})).call() has a real and reasonable advantage over doSomething(.{arg1=1}).

If the number of parameters is very large, we may need to write doSomething(&.{arg1=1}) at the call site to avoid copying, because PRO is going to be removed. The call method can allow the caller to worry less about such things.

However, in any case, parameterized structs may cause parameters that could have been passed through registers to be forced onto the stack, but of course, this is not a big deal.

2 Likes

Thanks for this, will think a bit…

I vaguely remember reading that the zig compiler will decide when to pass by pointer vs copying based on things like the struct size, even if you don’t explicitly pass by pointer?

2 Likes

good thing you mentioned this. I was passing pretty large structs by value with my above assumption.

Not a huge deal, but I was hoping I could rely on registers for small structs. Any docs I can reference on when structs as parameters would be copied on the stack vs passed through registers

i pressume the same decision making of passing in registers if there is space (< 16bytes most of the time ??) is also relevant for normal function params, i.e. if there are too many it anyways gets copied to the stack?

I don’t use arguments popups, I open the definition of the function in a split:

9 Likes

The default function calling convention in Zig is .auto, which doesn’t have a fixed specification. However, since PRO was removed, I suppose its function calling convention should be similar to the C ABI specified by the operating system.

The ABI for AMD64 Linux is System V (See 3.2 FunctionCallingSequence). Windows has a different calling convention. The ARM platform has the AAPCS64 convention.

But note that in Zig we basically use the .auto calling convention, so only export functions are guaranteed to be consistent with the system ABI.

1 Like

Yes, it does not work. But zls seems to know about the type expected for .init.
I cannot find an issue here: Issues · zigtools/zls · GitHub please consider adding a new issue.

Thanks for confirming. Added the issue: Field auto-completion for function parameter structs · Issue #3196 · zigtools/zls · GitHub

3 Likes