Threads for rcalixte

    1. 3

      This looks interesting. Did you use a tool to automate writing the bindings? How long did it take? Also, would it be possible to setup a GitHub action that generates the shared object files? I’m not sure if this is possible if the build takes more than 1 hour though.

      1. 8

        The code to generate the bindings is included under cmd/genbindings. It’s written in Go since that is what the upstream was based on and it will likely stay that way so that bidirectional collaboration can occur for a toolkit as large and complex as Qt.

        This… took a while. Various iterations, starts and stops, bouts of burnout, etc. etc. These libraries are far from complete and there’s tons more in the backlog that isn’t included in the release. There’s a few comments in the code for things I was trying and other in-progress pieces sprinkled throughout.

        CI & CD are part of the plans for these projects and are kind of a hard requirement before code contributions can happen due to the large size of the code base. GitHub’s runners should be able to complete the build for these in under 20 minutes though the free public runners have a limitation of 4 vCPUs. That remains to be tested.

        I don’t know if folks would want to consume object files from the repository or if they would rather receive them from their distro or compile them locally themselves. I tried to structure the libraries so that it would be easy for distro maintainers/packagers to pick up. I also think these are better as static libraries than dynamic ones since the development process will likely be very local/specific due to the Qt versioning variations available in the wild. I would love to hear what folks think.

      2. 8

        Looking through the code, this was a pretty neat exercise. The results are great too.

          1. 6

            Does it support sixel graphics? What has kept me on foot has been that yazi is able to preview images in the terminal.

            1. 8

              Ghostty uses the Kitty graphics protocol instead of sixel.

              1. 5

                Thanks, that would be one reason for me to stick to Wezterm, as tmux supports sixel but not the Kitty protocol.

            2. 5

              @mitchellh Since you emphasized platform-nativeness, I wonder if you’ve done any work with the platform accessibility APIs for the main terminal widget. Blind users often end up using other solutions, such as Speakup on the Linux test console or a screen reader inside the terminal, but I think there are other reasons to support the accessibility APIs, such as support for fine-grained cursor tracking in screen magnifiers.

              Also, forgive me if you’ve answered this elsewhere, but on Linux, are you using GTK 3 or 4?

              1. 12

                Ghostty 1.0 likely won’t have any differentiating accessibility features, but utilizing platform-native accessibility APIs is absolutely a goal. There is a large issue on it in the [currently private] Ghostty repository which you can read once it’s public.

                Based on maturity and availability, it looks like right now the focus of any accessibility work will likely be within macOS, but it’s too early to make a final decision since it’s still under discussion.

                1. 5

                  It’s GTK 4 with libadwaita if available.

                2. 1

                  I’ve done this with a few devices including the throwback Nexus 10. I prefer to keep Battery Saver and Airplane modes enabled unless I’m installing software updates or transferring files to the device. Moon Reader Pro is my go-to there after years on Manjaro. All in all, it works rather well.

                    1. 10

                      As someone who does not know the Zig community at all, I am curious about the status of this issue you pointed out. (Summary: there is a flaw in the current specification of Zig, the behavior is surprising in bad ways, and people are arguing about how to fix it without sacrificing performance too much)

                      • in the issue you point out (#12064, posted in July 2022), there is a post from the Zig BDFL pointing out that this is a serious issue that should be fixed
                      • a design to solve this issue has been proposed in #12251, also posted in July 2022, and it looks reasonable, written by someone who is skilled with Zig, and is good at explaining a design and providing concrete examples alongside it

                      The proposed solution does not appear to be gaining traction, and there is no clear message in that second issue indicating that Zig maintainers are interested in it. The discussion stalls in July 2022, then it starts again in July 2023, inconclusively, and then basically nothing. What is the current status and why does this issue, which appears to be solve a problem identified as important, look abandoned?

                      1. 25

                        My reading of the situation:

                        Zig is at version 0.13. It has bugs in the standard library, in the compiler, and in the language itself. The contract with the users is that you’d have to deal with that! If you are not ready to debug your toolchain, then you probably shouldn’t be seriously looking at Zig just yet.

                        You can prioritize shipping something useful to users, or slowly building the perfect solution. Zig tilts heavily towards the second option — it loves solving problems rather working around them.

                        While #12251 is a reasonable looking solution to the problem, it doesn’t look like the perfect move. The ideal situation here would of course to just make it a compilation error, and to make the user insert temporaries explicitly, when necessary. My reading is that it is not clear, at this point, whether a better solution is possible. There’s also a desire to do https://github.com/ziglang/zig/issues/1108, which would have major implications for this case as well.

                        Importantly, the first-order practical impact of this issue is close to zero: I’ve been writing Zig full-time for almost two years at this point. I discovered this issue(well, this class of issues) from first principles on like my day 2 with Zig: https://github.com/ziglang/zig/issues/5973#issuecomment-1330743975. I am yet to hit the single case where it bites me in practice. That’s just isn’t happening in the kinds of code I write. I spend way more time on various os-integration OS bugs. Eg, https://github.com/ziglang/zig/issues/18520 is waaay more important for me now.

                        So for me, it makes total sense that fixing this issue is not a priority, in the absence of clearly good solution. Just let it simmer! Maybe a better fix will emerge!

                        At the same time, I am very worried about second order effects here. This goes to the heart of the aliasing model of the language, without which you can’t actually compile anything for real. And, at least until the strict provenance work, I don’t think we had anything close to a satisfactory model of aliasing. So this issue is absolutely a blocker for stabilizing the language, and needs a good solution. But is a good solution possible without brining in all the complexity of the borrow checker? I don’t know, and this what worries me on the next-ten-years horizon.

                        1. 7

                          So for me, it makes total sense that fixing this issue is not a priority, in the absence of clearly good solution. Just let it simmer! Maybe a better fix will emerge!

                          I have a different take. A good solution has been proposed, that ensures correctness in all cases but sometimes degrade performance, and looks fairly simple to implement. If people haven’t integrated it in two years, they are uncomfortable about something: from the outside, it looks like they favor performance over correctness. I don’t know if this is a core value of the project, or just what we observe on that one issue. But I would be hesitant to invest in an ecosystem that favors performance over correctness, because in my experience that comes back to bite people in major ways.

                          (You of course know much more about the language than I do. Probably you have a more informed take and everything is fine. But it might also be the case that your opinion is biased by your familiarity and attachment to the project, making you minimize things that are worrying.)

                          1. 9

                            I mean, Zig’s not memory safe! It absolutely does value performance and simplicity over compiler-enforced safety. It is very reasonable to expect that the compiler should prevent you from messing the memory terribly in 99.9% cases, and not just 90%. Zig is at 90% even outside bugs.

                            But it might also be the case that your opinion is biased by your familiarity and attachment to the project, making you minimize things that are worrying

                            I hope that this is somewhat counter-acted by my equally-strong if not stronger attachment to Rust, which does have a very different stance here :D

                            1. 9

                              This is not about memory safety in user programs, this is about programs having a predictable, expected, non-surprising (possibly unsafe) semantics. We need this even when we program in an unsafe language, and possibly even more so than when we program in safe languages as bugs typically have a higher cost/severity.

                              1. 5

                                Duno, this seems literally the same issue as passing overlapping pointers to memcpy.

                                The current observed behavior is fully in-line with documented semantics of language: https://ziglang.org/documentation/master/#Result-Locations

                                This could be surprising if you are coming from C, but, if you think from first principles, I think Zigs semantics is actually natural and it’s C who is the weird one.

                                1. 12

                                  Duno, this seems literally the same issue as passing overlapping pointers to memcpy.

                                  It is not literally the same issue. When you call memcpy, you explicitly pass in a destination pointer with the expectation that it will be used mutably. That fact is clearly visible at both the declaration and use sites, and if you pass the destination pointer indirectly through multiple layers of functions, it will be visible at each of those functions’ declaration and use sites as well. In contrast, the effects of Zig’s result location semantics can reach across an arbitrary number of function calls, entirely implicitly, and small changes at any point in the chain which would not normally have any effect (such as introducing or removing a temporary variable) can have an impact on the behavior of struct assignment.

                                  The current observed behavior is fully in-line with documented semantics of language: https://ziglang.org/documentation/master/#Result-Locations

                                  I downloaded the latest version of the Zig compiler earlier today to run some experiments, and at first I was actually unable to reproduce the surprising behavior that I originally saw in 2022. Apparently, a change was made about a year ago such that struct initialization expressions with an explicit type no longer participate in result location semantics, i.e.

                                  s = S{ .x = s.y, .y = s.x };
                                  std.debug.print("{} {}\n", .{ s.x, s.y });
                                  

                                  now prints “2 1”, but

                                  s = .{ .x = s.y, .y = s.x };
                                  std.debug.print("{} {}\n", .{ s.x, s.y });
                                  

                                  prints “2 2”. I consider this to be even worse than before, as not only is there now another way in which a small, hard-to-spot difference in program source can have a surprising effect on struct assignment behavior, there is now a track record of that behavior changing silently between Zig releases as well.

                                  This could be surprising if you are coming from C, but, if you think from first principles, I think Zigs semantics is actually natural and it’s C who is the weird one.

                                  I wonder if you actually believe this. Zig’s behavior here is not only surprising coming from C, it’s surprising coming from essentially any other programming language under the sun with compound types and an assignment operator, it’s surprising given the way assignment is used in pseudocode descriptions of algorithms, and it is surprising from first principles.

                                  1. 3

                                    I wonder if you actually believe this

                                    Yup, I learned that C semantics is wrong even before starting with Zig, from https://outerproduct.net/boring/2021-05-07_abi-wrong.html.

                                    The belief was further confirmed when I learned that both Zig and Carbon are not going with C semantics.

                                    1. 7

                                      This is not a problem with C’s semantics, though- merely with typical implementations of those semantics. The semantics we want, and which both C and Carbon implement, but which Zig violates, are for by-value bindings to be independent of each other. This is the entire meaning of “by value” and its implications for equality and assignment.

                                      The post you linked aligns with this! It does not propose a change in C’s semantics, but a change in the ABI to that reduces copying while preserving those semantics. Carbon, likewise, merely restricts the semantics of by-value parameters to a subset of C’s, without changing the meaning of what remains.

                                      The reason you are getting so much pushback on this is that it would be extremely straightforward to retain value semantics with various combinations of a) making some bindings immutable (visibly like Carbon, or invisibly like your link proposes) and b) inserting copies in a few more places, while still leaving open the option of improving things in the future. It is simply not a deep enough problem to warrant arguments that value semantics are somehow surprising from first principles.

                                      1. 2

                                        I don’t think we ever want by value. We want “pass by whatever, let the compiler decide between by-reference or by copy, but also verify that the result doesn’t change between the two versions). If you need by value semantics, the right way to express that is to create the copy explicitly in the callee.

                                        Similarly, for returning, it makes so much more sense if the value is just directly constructed where it should end up at, again, provided that compiler actually checks that there’s no problematic aliasing.

                                        The bug is aliasing, adding implicit copies hides the bug, not fixes it.

                                        extremely straightforward

                                        My understanding is that this is still is open question for both zig and Carbon at least ( https://lobste.rs/s/syegk7/carbon_copy_newsletter_no_3#c_ymynyl)

                                        1. 6

                                          You are totally missing this distinction: “by value” is a semantics, and “by pointer”/“by copy” are implementations. If you have verified that the choice of implementation does not change the results, then you have value semantics.

                                          Carbon’s semantics are not an open question at all: their implementation is conservative enough to preserve their chosen semantics, with an open question of how to improve that implementation over time.

                                          Zig’s approach is instead refusing to choose a semantics at all, and allowing themselves to silently change program behavior over time. What would be straightforward would be to pick a fixed semantics that is actually reasonable (whether or not that is vanilla value semantics, or a limited subset of it that may or may not expand over time, or something totally different but chosen on purpose) and then preserve compatibility with it as the implementation improves.

                                        2. 2

                                          No, I am talking about semantics. If you have a generic function that accepts x: T, then:

                                          • if T is a small value, it should be passed by value and address should be meaningless
                                          • if T is an immovable type, it should be passed by reference, and stability of interior pointers should be guaranteed.

                                          Similarly, if you are returning something, and that something is immovable, you must be able to rely on the fact that the thing is constructed in place.

                                          This is not by-value semantics, by-value doesn’t work with immovables.

                                          This is also not by-reference, because the meaningfulness of address is not guaranteed.

                                          1. 4

                                            This is not by-value semantics, by-value doesn’t work with immovables.

                                            This is where you are conflating semantics and implementation. The thing that is incompatible with immovables is the by-copy implementation strategy.

                                            Value semantics (i.e. bindings and intermediate expressions are independent; the result doesn’t change between by-pointer or by-copy) are perfectly compatible with this sort of implementation strategy, so long as it is done soundly.

                                            Zig is letting the tail (the implementation) wag the dog (the semantics) here.

                                            1. 2

                                              Hm, I am still convinced that this is a semantic difference. With value semantics, x = f(x) is legal. I think this should be illegal. The semantics that allows that code through the implementation that inserts explicit copies is not the semantics I want.

                                              1. 5

                                                Yes, this is this sort of thing I meant by “a limited subset that may or may not expand over time.” You can certainly say that situations like this where C would have to introduce a copy should instead be errors, and expect the user to write the copy themselves- that preserves the binding independence we care about.

                                                Carbon does this in a slightly different spot- you can’t take the address of a parameter without declaring it a var or making a local copy of it. This is a much better solution than simply allowing the desired semantics to be violated if you happen to step outside the boundaries.

                                                Of course there are some language features, like general pointers, where it takes a heavy hammer, like borrowck, to rule out the violations. The problem here is that it does not require anywhere near that level of complexity to rule out the stuff we’re talking about. And Zig doesn’t even try to detect the most obvious cases where everything is local!

                                                1. 2

                                                  I don’t believe Carbon has a full solution for this problem.

                                                  My understanding is that their semantics for the following problem is TBD:

                                                  fn F(x: i32, y: i32*) -> i32 {
                                                    *y =  92;
                                                    return x;
                                                  }
                                                  
                                                  fn Main() -> i32 {
                                                    var x: i32 = 0;
                                                    return F(x, &x);
                                                  }
                                                  

                                                  This currently gives a RUNTIME ERROR: <source>:5: Reference has changed since this value was bound. in the reference interpreter.

                                                  And I personally don’t know what is the good solution here:

                                                  • You could implement the entire borrow checker, but then you are implementing the entire borrow checker
                                                  • You could forbid first-class pointers, and then borrow cheeking becomes fully local and syntactic
                                                  • You could declare this legal and introduce copies, but then you are introducing copies, and, I argue, hide bugs instead of making them jump out
                                                  • Maaaaybe there’s some fancy runtime mitigation that works good enough?
                                                  1. 4

                                                    I’m not trying to argue that Carbon has a full solution for this problem, but I’m also not sure why it matters that explicit pointer operations are able to influence other bindings- that is one of the main reasons people use pointers in the first place. Pointers are the controlled way to step outside of value semantics.

                                                    As far as this example goes, though, you could forbid it locally in the same way as x = f(x). The local var x is clearly not immutable, so you clearly cannot use the “pass by immutable pointer” strategy with it. This would be an easy place to require an explicit copy.

                                                  2. 2

                                                    The problem here is that it does not require anywhere near that level of complexity to rule out the stuff we’re talking about.

                                                    To triangulate this a bit, I obviously agree with that: there’s an easy 80% partial solution here, like #12251 which started this discussion thread. I am adamant though that there isn’t known easy full solution here.

                                                    I am totally OK with people claiming that this is a huge issue in practice, and that some kind of lint, runtime check, or just extra copies in the codegen would help them. This is not what I personally experience with this issue, but I am totally willing to believe that others step into this regularly.

                                                    What I am not OK with is are the claim that the full, principled fix is somehow trivial and obvious here.

                                                    1. 6

                                                      The claim is not that Zig can trivially get to its ideal end state. The claim is that Zig can trivially plug the hole with a sub-optimal implementation, without closing the door to its ideal end state.

                                                      I think even just a conservative error, or dumb runtime checks in checked builds, would be sufficient here. Anything that actually makes it clear that the semantics are supposed to be “pass by value, but you bring the copies” instead of “we used the syntax of pass by value but we implemented pass by reference, lol.”

                                                      1. 2

                                                        Yeah, I agree with all of this, with a clarification that this particular issue is not that important for me personally in practice: https://github.com/tigerbeetle/tigerbeetle/issues/1191

                                                        1. 4

                                                          Ah, so when you say you never hit it in practice, you mean you’ve actively worked around it by simply avoiding pass-by-value syntax altogether:

                                                          We currently avoid pass by value syntax altogether, and always use *const T or *T.

                                                          1. 2

                                                            That’s a work-around for the opposite end of this issue :D!

                                                            A couple of times we hit problems where Zig introduced copies of large structs on the stack, which hurt performance. We worry about that way more than about aliasing breaking our code, to the point of writing memcpy-finding tool:

                                                            https://tigerbeetle.com/blog/2023-07-26-copy-hunting

                                                            But the general point is correct: TigerBeetle is not normal code, we have highly unusual architecture and highly unusual testing, to the point where Zig makes more sense that Rust. I am explicitly not claiming anything about “average program”.

                                          2. 3

                                            That article appears to have absolutely nothing to do with Result Location semantics?

                                            1. 3

                                              Zig’s RLS is exactly as surprising as Zigs parameter passing. These are just flip sides of the same idea. The article describes the parameter passing part of this.

                                              Both Zig and Carbon have RLS and parameter passing differences with respect to C

                                              1. 1

                                                RLS in general is not the same thing as Zig’s specific RLS, and RLS being a good idea isn’t the same thing as Zig’s specific RLS being correct. As already noted by @glowcoil, how it works appears to have even silently changed in a subtle way.

                                                1. 4

                                                  Yes, 100% of participants in this discussion, and, I think, 100% of Zig developers agree that the current behavior is surprising and something needs to change.

                                                  The disagreement is over:

                                                  • whether this is a deal-breaker issue which warrants immediate fix, or whether this is a small paper cut, which is not that important given all other problems that Zig has.
                                                  • whether there is an obvious fix which just needs to be implemented, or whether it is a hard open language design problem.

                                                  My position is that:

                                                  • first order practically, this is a paper cut
                                                  • second order systems-thinking, this is a deal breaker
                                                  • this is an extremely deep open problem that goes to the heart of the semantics of low-level language.
                                            2. 2

                                              I think that article is based on an incorrect model of how languages are supposed to map to the ABI. Since the 1980s C lets the programmer choose pass-by-copy or pass-by-reference (except for arrays, of course) so it seems like a regression to 1970s K&R1 to say pass-by-copy should not be allowed for big structs.

                                              The problem with passing by immutable reference is that it fails horribly if the object turns out to be mutated. Zig is asking for trouble when it combines passing by immutable reference and returning by mutable reference. It ought to reject any function call where the return location obviously aliases an argument location, because it knows the return location must be mutated and the argument location must not be, which is a contradiction. (There will be cases where aliasing isn’t obvious to the compiler, but that’s a more familiar kind of unsafety.)

                                      2. 3

                                        What has drawn you from Rust to non-memory-safe Zig to the point of “full-time” per above?

                                        1. 7

                                          I don’t care about the language that much, as long as it is reasonablish. I care about what I am writing. And it so happened that my dream systems programming project, TigerBeetle, is in Zig, rather than Rust or C.

                                          I also believe at 0.8 that Zig is a significantly better choice than Rust for TB, but I would have been writing Zig even if believed at 0.8 that Rust would have been a marginally better choice.

                                          A bit more context in https://matklad.github.io/2023/03/26/zig-and-rust.html

                                    2. 3

                                      I think from pretty much everybody who uses Zig I hear that this is not much of a problem in practice. It does however greatly puzzle me how this is not something that happens in real world code much. I also ran into this issue when I played around with Zig very quickly and assumed that this would be problematic but seemingly not.

                                      1. 5

                                        I don’t write real world code, TigerBeetle is a world onto its own XD

                                    3. 5

                                      I recommend watching Attack of the Killer Features from SYCL 2023 by Martin Wickham (SpexGuy, the author of #12251). This was also discussed on lobste.rs last year. I’m confident the Zig team will figure out a satisfactory solution to this. I personally don’t read too much into the lack of status updates on that issue. Perhaps it’s not a top priority because they know a solution is mostly sketched out.

                                      1. 4

                                        Zig core team member here – I might be able to shed a little light on this.

                                        The important thing to understand here is that a proposal not being accepted nor actively commented on does not mean there is no interest in it. I can’t speak for other core contributors, but I would consider #12251 to be a good proposal which solves the stated problem in a pretty optimal way, and personally hope it will be accepted at some point (of course, unless a better solution is proposed!).

                                        The reason you see no activity for the issue is, well, pretty boring – we haven’t gotten around to it yet. Zig has 650 open language proposals, over 500 of which are not accepted (i.e. are undecided). This proposal is of a pretty low priority, because while the current behavior is definitely unintuitive and kind of a footgun, it’s actually incredibly rare to run into. I’ve been writing Zig for around 4 years and have not once been burned by this. That might seem surprising – I also thought this was a huge problem when I first saw it several years back! – but in practice you just don’t hit this situation much. That doesn’t mean we’re not going to address it – status quo is unsatisfactory in my opinion, and I think Andrew still shares that opinion (though I don’t want to speak for him). But the priority is low simply because this isn’t really a practical problem to current Zig users or to the core team.

                                        This proposal is also complicated by the fact that it ties into some core features of Zig which are somewhat in flux; namely, RLS (the language feature underlying this footgun) and pointer aliasing (a highly related concern). This is an area of active design work for Zig, so it would be silly to accept a proposal which may not end up aligning with other language changes in the future. Instead, we need to analyze these semantics more broadly, and decide the direction the language should take. Perhaps RLS and aliasing rules will exist mostly unchanged from today, and #12251 will be a good fit to avoid the confusion RLS can lead to; or perhaps we’ll redesign RLS in some way where other solutions need to be considered.

                                      2. 9

                                        Okay, I’ll bite. If you’re coming from C, this isn’t a syntax used for swapping values. Outside of struct fields, swapping is also currently not supported syntax. It seems to me like the proper thing to do is declare that swapping is not supported in this language and move on. (Assignment occurs in the moment that it is presented.) That might rub people coming from higher-level languages the wrong way. The alternative then is to somehow have the compiler detect this… but across multiple types, that gets complex to implement without hidden allocations.

                                        The “fix” also seems to be trivial: writing your own (and proper) swap function (bitwise operations, integer arithmetic, temporary variables, memory address swaps, etc.). I would be curious to know more about how this is a blocker to adoption or of any urgency for you.

                                        1. 27

                                          I don’t understand your reply – I think that it is incorrect. In C, this works as intended:

                                          #include <stdio.h>
                                          
                                          typedef struct { int x; int y; } point;
                                          
                                          int main(void) {
                                            point p = (point){ .x = 0, .y = 1 };
                                            p = (point){ .x = p.y, .y = p.x };
                                            printf("Swapped: {x = %d, y = %d}\n", p.x, p.y);
                                            // prints: Swapped: {x = 1, y = 0}
                                            return 0;
                                          }
                                          
                                          1. 15

                                            please read the issue thread & linked discussions; this isn’t specific to swapping struct fields.

                                            the blocker for me, personally, is that the semantics of Zig are still baking & the language team is figuring out exactly what their preference is for correctness in general & in the context of tricky optimizations in particular.

                                            this example is meant to demonstrate that “gotta go fast” overrides a thoughtful examination of rvalue references.

                                            1. 9

                                              It seems to me like the proper thing to do is declare that swapping is not supported in this language and move on. (Assignment occurs in the moment that it is presented.) That might rub people coming from higher-level languages the wrong way.

                                              I believe you completely misunderstood what is going on. In most “low level” languages, a struct instantiation is going to full occur before the struct is assigned to anywhere. So s = S { .y = s.x, .x = s.y }; would first construct a new S struct using the values currently in the variable s, and then assign the new struct to that variable. This is a result of Zig’s Result Location Semantics, which has created many odd footguns in the language. It isn’t just swapping, any instance of a = f(a,b,..) can fuck your shit up.

                                              1. 2

                                                Is a = f(a) && b() || c() safe? The reason why I’m thinking that it might be is that if I wrote this in C, then I believe the evaluation of either b() or c() would be on the opposite side of a sequence point from the read from a in f(a).

                                                1. 1

                                                  On the other hand, there’s no footgun with a.* = f(&a, &b, ....). Are the sigils intrinsically helpful here, or are we just accustomed to those semantics? If I can get used to recognizing what’s going on there, I think I can also get used to recognizing it without the sigils.

                                                2. 3

                                                  The “fix” also seems to be trivial: writing your own (and proper) swap function (bitwise operations, integer arithmetic, temporary variables, memory address swaps, etc.). I would be curious to know more about how this is a blocker to adoption or of any urgency for you.

                                                  The problem isn’t that it’s possible to write code that does the expected swap; it’s that it’s possible for someone else (who might well not be you) to write code that seems like it does the expected swap but actually does something subtly wrong.

                                                3. 4

                                                  These seem kinda superficial. Looking at that code I couldn’t tell which behavior would be more intuitive tbh.

                                                  1. 6

                                                    The example lower down the thread about it doing this from input to return value across functions is the much more egregious example to me. It’s not just “field initialization is processed in textual order”, but that struct value and return semantics aren’t maintained by the compiler at all.

                                                4. 1

                                                  This is overly complicated. You can achieve this using Zig’s toolchain[1] (no need for the language) and all you need to do is modify the environment variables to switch the C (and/or C++) compiler. No need for containers and Nix.

                                                  1. https://dev.to/kristoff/zig-makes-go-cross-compilation-just-work-29ho
                                                  1. 4

                                                    BTW, also keep in mind that the Nix approach works anywhere. There is a reason I didn’t put Building static CGO binaries with Nix.

                                                    1. 1

                                                      You seem determined to use Nix. That’s fine but keep in mind where you are adding in unnecessary complexity. Nix is a package manager and I’m sure there is a package for zig. Wouldn’t that make the process and therefore the maintenance easier?

                                                      1. 7

                                                        Nix is a build system; not a package manager. On NixOS, you manage packages by building a new system (as opposed to mutating an existing system, like a package manager).

                                                        So the comparison is between build systems: Zig’s and Nix. It seems highly relevant to then compare their ability to manage dependencies.

                                                        1. 5

                                                          I don’t think this is “adding in unnecessary complexity”. If Zig works for your project fine, but it wouldn’t work in this case and I don’t want to create my own way to manage dependencies (this would be “adding in unnecessary complexity”).

                                                          Wouldn’t that make the process and therefore the maintenance easier?

                                                          I don’t know how you think managing dependencies correctly is, but something that I generally imagine is:

                                                          • Having some way to declare dependencies by platform
                                                          • Make sure that we are getting the expected source code (e.g.: check hashes)
                                                          • Build all dependencies, that probably have different build requirements and build systems (e.g.: autoconf, cmake, waf, etc.)

                                                          Those are definitely not trivial, and something that Nix gives you for free. So yes, maybe for a small project you can get by hacking some scripts with Zig, but anything more complicated and I would say Nix quickly become the best option.

                                                          1. 1

                                                            You’ve found a hammer that you really like but that does not make everything a nail. For resolving package dependencies, use a package manager (Nix or otherwise). This is a solved problem. For compiling Go with Cgo where you want to maintain a static binary or cross-architecture compilation, the Zig toolchain has emerged as the simplest solution to date. The package manager and build tool do not have to be the same hammer where it introduces additional complexity.

                                                            Edit: Zig can also be used as a package manager[1] but I do not have current experience with this.

                                                            1. https://github.com/allyourcodebase
                                                            1. 3

                                                              You are putting words in my mouth. I like Nix, but I don’t use everywhere. There are lots of projects that I don’t introduce Nix if I don’t have too, one example is this blog, that uses a pure Go solution to generate the posts. And the reason I selected Go is exactly because I didn’t want to use Nix for e.g.: managing Python dependencies, that was the previous version of this blog.

                                                              But you just said “Zig is less complicated” without understading the whole issue here, and you clearly don’t understand the issue yet. No, managing package dependencies WITH static/cross-compilation is not a solved problem in Zig, and just saying “use a package manager” while also using Zig toolchain will not solve the problem magically. Yet Nix solve both problems.

                                                              The package manager and build tool do not have to be the same hammer where it introduces additional complexity.

                                                              Even Zig itself seems to understand that you can’t separate those 2 issues unless the only thing you’re interested to build is e.g.: hello world, because they’re also implementing a package management system.

                                                              But from a quick look at it, it is a moving target (breaking changes), with a low amount of packages ready (so if you want to use, you need to package your own libraries). So definitely Nix looks a more mature solution, at least for now. Maybe Zig will become a good solution for this problem in the future.

                                                              1. 0

                                                                The linked comment is three years old. Go was fixed as of 1.18 and the currently supported versions are 1.21 and 1.22. That is to say that with supported versions of Go, this is a solved problem.

                                                                No, managing package dependencies WITH static/cross-compilation is not a solved problem in Zig

                                                                It actually appears to be. Looking into it more, as of Zig 0.13, you would accomplish this with two files: build.zig and build.zig.zon. The former is written in Zig and the latter is a Zig struct. (ZON is Zig’s version of JSON.)

                                                                I wish you luck and growth in your future endeavors.

                                                                Edit: Here is a YouTube link to a talk from 2022 (so some things have changed for the better) on this subject that I think you would enjoy. He accomplishes the same task and with Nix. Link

                                                                1. 4

                                                                  A few words on Nix vs Zig, in terms of Building Static Binaries

                                                                  I have a GH project specifically for compiling static binaries: https://github.com/Azathothas/Toolpacks (I compile several thousand static binaries) While I understand & love zig’s awesome tooling, overtime I have moved on from using zig at all. Main reasons:

                                                                  1. Wherever I had a need to use zig, I found I was much better off using an alpine container & simply pulling in static libs, because with zig there was no simple way to link to static libs. (It’s not practical to have a build.zig file for hundreds of binaries)
                                                                  2. Using Nix then, was another paradigm shift as I no longer needed to use containers and compile static libs as nix did it all for me with one single one-liner.

                                                                  Taking a quick look, I have about 25 instances of calling nix-build (Most of them would have required complex build steps AND external libs if I used a container, but with Nix, it’s a one-liner)

                                                                  And 47 instances of using zig (None of them require complex build steps OR external libs, hence no need for Nix/Containers)

                                                                  There used to be 0 instances of nix just a few months ago, but it’s slowly risen. While, use of zig is going down as tools have started to require external libraries. This is being replaced with alpine containers or nix.

                                                                  1. 4

                                                                    Edit: Zig can also be used as a package manager1 but I do not have current experience with this: https://github.com/allyourcodebase

                                                                    Nice, but I think this also shows how hard the problem is: they have barely 20 libraries (it was so low that I could just go to their profile and count) that they ported to the Zig build system. nixpkgs on the other hand has 110k+ packages 1. Of course not everything there is a library, but the point still stands: Zig will be significantly more complicated to setup than Nix for anything that has more than a dozen of libraries as dependencies.

                                                                    Edit: Here is a YouTube link to a talk from 2022 (so some things have changed for the better) on this subject that I think you would enjoy. He accomplishes the same task and with Nix. Link

                                                                    Sorry, I took a quick look at the video but did not find where he compares Zig and Nix for CGO compilation, maybe I missed something.

                                                                    The presenter does uses Nix, but just to load a GCC compiler, that is not the most interesting use of Nix.

                                                                    You’ve found a hammer that you really like but that does not make everything a nail.

                                                                    It is looking like you found a hammer and are trying to apply to everything, while not understanding the actual problem (you even said yourself “I do not have current experience with this”).

                                                                    Zig definitely is nice and interesting, but it is not the panacea you are making it to be. Neither is Nix, but I would argue that right now Nix is significantly more powerful than Zig for this problem, be it by being a solution that is already much more mature, be it because it has way more packages.

                                                                    1. 3

                                                                      The linked comment is three years old. Go was fixed as of 1.18 and the currently supported versions are 1.21 and 1.22. That is to say that with supported versions of Go, this is a solved problem.

                                                                      I linked the wrong comment, sorry: https://zig.news/almmiko/building-zig-libraries-with-c-dependencies-25a#comment-14o. I blame my mistake in macOS, I hate that it uses Cmd+C/V instead of Ctrl+C/V to copy-and-paste.

                                                                      It actually appears to be. Looking into it more, as of Zig 0.13, you would accomplish this with two files: build.zig and build.zig.zon. The former is written in Zig and the latter is a Zig struct. (ZON is Zig’s version of JSON.)

                                                                      It works, but as I showed with the correct comment above, it is a moving target that has breaking changes in Zig trunk it seems.

                                                                      And before you say that this is normal because 0.14 is the current trunk, it seems that something also broke between 0.11 and 0.12: https://zig.news/almmiko/building-zig-libraries-with-c-dependencies-25a#comment-13n.

                                                                      While I appreciate the efforts of Zig team, it looks to me that this is still too new to be relied for anything. The equivalent in Nix, pkgsCross and pkgsStatic, didn’t change since I started learning Nix (7 years ago).

                                                                      I wish you luck and growth in your future endeavors.

                                                                      Same for you :).

                                                          2. 3

                                                            okay i’ll bite: basically every time someone comes out and advocates for how Zig solves some historically complex problem everyone describes as intractable the thing they’re pointing at doesn’t actually solve the historically complex problem everyone describes as intractable.

                                                            in this case, everyone likes to point about how zig cc just magically solves cross-compilation, but it doesn’t! “all” it’s doing is the (admittedly difficult!) work of papering over the unpleasant UI that C toolchain cross-compilation has evolved into over the years.

                                                            that’s great, zig cc is pleasant chrome that you can use if you’re cool with zig cc being your C toolchain entry point & the software you have to build is fairly simple! as long as you’re on the happy path that the Zig developers have curated for you then things will probably be super pleasant!

                                                            the problem i have with this is that it’s so easy to wander off this happy path when you are doing anything involving cross-compilation that by just recommending zig cc and claiming it fixes everyone’s problems you’re basically setting them up for failure as soon as something goes wrong.

                                                            like… if your problem is solved by depending on Zig’s “solution” of shipping a subset of glibc, musl, and mingw that’s great, but IME most people who want to build executables that depend on static archives are going to maybe possibly run into these issues sooner rather than later.

                                                            EDIT: also worth noting that the process for bumping the libc files that Zig bundles kind of demonstrates my point about there being no free lunch here.

                                                            1. 2

                                                              It’s not about using Zig as a C/C++ compiler. It’s about using Zig as the C/C++ compiler specifically when compiling Go with Cgo. That specific problem. Without Zig as a toolchain, by default Go will switch to dynamic binaries instead of static ones, also removing the ability to build cross-platform trivially as well. Previously, one solution was to use Docker containers in a matrix to restore this. By introducing Zig’s toolchain to the mix, compiling Go with Cgo and having static, cross-platform, and cross-architecture builds becomes trivial again. No Docker containers and no complex configurations.

                                                              Here is a decent write-up on the topic: https://www.uber.com/blog/bootstrapping-ubers-infrastructure-on-arm64-with-zig/

                                                              What happens once you start using Zig’s toolchain is that it becomes trivial to expand that. First is often writing a build.zig, which is written in Zig. And now, you’re writing in Zig. The toolchain is something of a gateway drug but at the same time, if you’re familar with Go, it solves a long-known problem and trivially. There’s a reason a lot of the Zig adopters have prior experience with Go. (Obviously there’s crossover from other languages as well.)

                                                            2. 3

                                                              What about the libraries? How to you compile e.g.: alsa-lib with Zig’s toolchain? Let’s say alsa-lib depends in 5 other dependencies, and each one of those depends in 5 other dependencies. Does Zig manage all those dependencies? Because this is something you get for free with Nix.

                                                              1. 1

                                                                Why would the compilation be different? And the Zig toolchain is not a package manager. Please take a look at the article since I think it will help you long-term.

                                                                1. 5

                                                                  When statically linking a binary you need to build all libraries statically too. This generally means having some way to manage dependencies, e.g.: downloading the versions and building them. This is something Nix gives you for free.

                                                                  I took a look in the article, and while it is interesting they never talk about dependencies. And because this is the hard part. I don’t expect this to be ever be done by Zig, but I am interested to be proved wrong, maybe Zig does have (or have plans) to manage this kind of dependencies automatically.

                                                                  1. 1

                                                                    It seems Zig does have a way to manage dependencies: https://zig.news/almmiko/building-zig-libraries-with-c-dependencies-25a.

                                                                    But from the comments it also seem pretty new, and had some recent breaking changes, so definitely not something that is production ready yet.