Live data from Hacker News

Zig; what I think after months of using it

strongly-typed-thoughts.net

141–150 of 181 posts

Re: Zig; what I think after months of using it

#141
post #108
post #92

Earlier quoted context omitted.

And on the whole, I prefer shadowing. I’ve never had a bug in either direction, but keeping everything immutable without shadowing means you spend all your brain power Naming Things.

I mean that's a really fake problem. How many times per line of code do you actually need to name variables and how many of those times you're shadowing a previously defined var. I'm guessing a very small amount.

It's not just the naming things, it's also what you do after you've named them—if you can't shadow a name then you are stuck both coming up with new names and sifting through all the existing names in your autocomplete to try to remember which one is the real one at this point in the code. Get it wrong? There's a bug.

That's not a fake problem, it's a problem I've actually run into on a regular basis on languages that don't have shadowing.

Re: Zig; what I think after months of using it

#142
post #108

Earlier quoted context omitted.

I mean that's a really fake problem. How many times per line of code do you actually need to name variables and how many of those times you're shadowing a previously defined var. I'm guessing a very small amount.

In case of rust, it actually happens quite often. I find myself rarely needing to use mut, instead using functional approaches such as iterators and expressions. So a high percentage of the code is let statements

Exactly. Shadowing is a super important complement to Rust's immutability. Without it immutability would be less useful and therefore less used.

Re: Zig; what I think after months of using it

#143

Earlier quoted context omitted.

> Replacing the value of one variable constantly throughout the code could lead to unpredictable bugs. Having variables with scopes that last longer than they're actually used and with names that are overly long and verbose leads to unpredictable bugs, too, when people misuse the variables in the wrong context later. When I have `initial_foo`, `foo_feature_a`, and `foo_feature_b`, I have to read the entire code caref…

I don't know zig at all, but why is the author trying to declare foo as const 3 times. Surely you would declare it as var with some default value that means uninitialized, then try and put values in it.

It's probably a Zig antipattern, but it's a very common Rust pattern. Shadowing in Rust allows immutability to be ergonomic, and lack of shadowing discourages immutability.

Zig isn't Rust, so it makes sense that patterns in Rust don't translate well, but also I totally get TFA's preference for Rust in this case.

Re: Zig; what I think after months of using it

#144
post #43

Earlier quoted context omitted.

> Replacing the value of one variable constantly throughout the code could lead to unpredictable bugs. Having variables with scopes that last longer than they're actually used and with names that are overly long and verbose leads to unpredictable bugs, too, when people misuse the variables in the wrong context later. When I have `initial_foo`, `foo_feature_a`, and `foo_feature_b`, I have to read the entire code caref…

> When I have `initial_foo`, `foo_feature_a`, and `foo_feature_b`, I have to read the entire code carefully to be sure that I'm using the right `foo` variant in subsequent code. If I later need to drop Feature B, I have to modify subsequent usages to point back to `foo_feature_a`. Worse, if I need to add another step to the process—a Feature C—I have to find every subsequent use and replace it with a new `foo_feature…

> When you have only one `foo` that is mutated throughout the code you are forced to organize the processes in your code (validation, business logic) based on the current state of that variable. If your variables have values which are logically assigned you're not bound by the current state of that variable.

If I'm understanding you right, this is just restating what I said as a positive thing. I stand by my assertion that it's not positive: you can always choose to leave previous states accessible by choosing different names. But if a language doesn't support shadowing then I don't have the capability to intentionally restrict myself from accessing those states. That means your language has less expressive power and fewer opportunities for me to build myself guardrails.

In some ways it's the opposite of unused variable warnings: if you disallow shadowing, the compiler is forcing you to leave variables accessible long after you need them. You're given no choice but to leave unused variables hanging around. With shadowing, I can choose the right path based on the situation.

> The only downside most people disagreeing with me are mentioning is related to ergonomics of it being more convenient.

As I said elsewhere, literally everything to do with programming languages is about ergonomics. Your arguments against shadowing boil down to ergonomics. You can't avoid having a debate by just saying "it's just ergonomics" when the debate that you started is which feature is more ergonomic!

Re: Zig; what I think after months of using it

#145
post #120

Earlier quoted context omitted.

FTrepo: Q: You didn't do X, so Zig will never be able to track X A: Maybe. Only way to know for sure is to fork this (or, hopefully, a 'real' successor) and fail. However, consider that "trivially" it should be possible to externally annotate every zig file with lifetime/type annotations identical to that of Rust and run "exactly the same" analysis as Rust and get the same memory safety as Rust. it appears the clr au…

> it appears the clr author anticipated you: you didnt fork it, try, and fail, so you have ceded the authority to credibly make your speculative complaint That's a caveat. Not an expectation. Plus that's not how proof works. Neither Zig nor Zig+Clr have really proven they are safe, ergo they are unsafe or possibly safe (respectively).

wow what are you afraid of. you're working really hard to tear down something that is an incomplete proof of concept.

Re: Zig; what I think after months of using it

#146
post #74

Earlier quoted context omitted.

It’s harder to write correct unsafe Rust than correct Zig because (1) Rust uses references all over the place, but when writing unsafe code you must scrupulously avoid “producing” an invalid reference (even if you never deference it), and (2) there’s lots of syntax noise which obscures what the code is doing (though &raw is a step in the right direction).

For Rust references, rules[0] are not hard to follow: The pointer must be properly aligned. It must be non-null. It must be “dereferenceable”: a pointer is dereferenceable if the memory range of the given size starting at the pointer is entirely contained within the bounds of that allocated object. Note that in Rust, every (stack-allocated) variable is considered a separate allocated object. The pointer must point to…

One reason it’s harder to follow is you can’t have references to uninitialized memory. In Zig pointers to uninitialized memory are fine as long as you don’t dereference them. That’s also true of Rust’s raw pointers, but most Rust code uses references, so it can’t be reused in unsafe contexts.

As a concrete example, I previously used Vec in unsafe code that dealt with uninitialized memory. This should be fine because Vec is basically a raw pointer and two integers. But later they changed the docs to say that this was undefined behavior (essentially, Vec reserves the right to “produce” a reference whenever it wants, even if you avoid calling methods that would do that). So my code that previously followed the rules now appeared to violate them.

Re: Zig; what I think after months of using it

#147

Earlier quoted context omitted.

> Huh? How do you think `const char s = "Hello"; const char t = &s[1];` works? So this is just an example of a pointer offset by 1 slot. It's not conceptually all that different to take sub-values of a slot. To work with values that consume fractions of a byte, such as a 3-bit integer, a pointer + a bit offset is used to grab that value (that complexity is abstracted by the compiler). My argument is the bit shift nec…

If your pointers have to have a bit offset, then now a pointer isn't just 1 hardware word, which would be odd to say the least for a systems language. (It would also be slow, because making a load anything other than a load is something you really don't want to do; you would lose the ability to fold into addressing modes on x86 in many cases for example.) If some pointers have a bit offset but others don't, OK, I gue…

Just to close the loop here, it looks like that is exactly what zig does for pointers, such that there is a different type of pointer for each combination of bit offsets and byte alignments, and yes it is distinct from a "regular" pointer (of which there isn't one "regular" type either, but also many separate flavors, including C-compatible, single-item, and many-item): https://ziglang.org/documentation/master/#toc-packed-struct

Re: Zig; what I think after months of using it

#148
post #82

Earlier quoted context omitted.

> It appeared to be on a good track to dethroning C++ in the domain I work in (video games)... but they locked it a while before figuring out the ergonomics to make it workable for larger teams. There are million-line Rust projects now. Rust is obviously workable for larger teams. > Any language that imbues the entire set of special characters (!#*& []{}(); ...etc) with mystical semantic context is, imo, more interes…

> I think you're talking about @ and ~ boxes. As I recall, those were removed the same year the iPad and Instagram debuted. Take criticism better. A language choice on a project means the veterans are indefinitely charged with teaching it to newbies. For all Rust's perks, I judge that it would be a time suck for this reason. Browsing some random rust game code: [ https://github.com/bevyengine/bevy/blob/8c7f1b34d3fa52…

I think this criticism is silly. Here's what your first example would look like in a language with keywords (where reasonable, perhaps like C#) instead:

  pub fn play(in out self, player: mut ref AnimationPlayer lifetime p, new_animation: AnimationNodeIndex, transition_duration: Duration) -> mut ref AnimationPlayer lifetime p
But, this is still confusing! Let's remove even more symbols, and make the syntax more obvious by removing abbreviations:

  PUBLIC FUNCTION Play
  LIFETIMES
    P
  PARAMETERS
    IN OUT Self
  Player AS MUTABLE REFERENCE TO AnimationPlayer WITH LIFETIME P
  NewAnimation AS AnimationNodeIndex
  TransititionDuration AS Duration
  RETURNS MUTABLE REFERENCE TO ActiveAnimation
  BEGIN
  ...
  END
IMO, using keywords instead of symbols for references, lifetimes, etc, would just make Rust overly verbose, and there's a reason BCPL used braces instead of BEGIN/END :^)

Re: Zig; what I think after months of using it

#149
post #108
post #92

Earlier quoted context omitted.

And on the whole, I prefer shadowing. I’ve never had a bug in either direction, but keeping everything immutable without shadowing means you spend all your brain power Naming Things.

I mean that's a really fake problem. How many times per line of code do you actually need to name variables and how many of those times you're shadowing a previously defined var. I'm guessing a very small amount.

It absolutely depends on the language and how heavily it encourages immutability. For example, Rust and Elixor allow shadowing.

An awkward middle ground for me is Kotlin. It allows shadowing, but warns, so it might as well not be allowed. So you end up using lots of scoping tricks to avoid either making everything mutable, or having dozens of nearly-identical variables.

Re: Zig; what I think after months of using it

#150
post #104

Earlier quoted context omitted.

Rust has ManuallyDrop, which is exactly the functionality you’re describing. It works just fine for those types of programs. The speed of the two is going to be largely dependent on the amount of effort that has gone into optimizing either one, not on some theoretical performance bound. They’re both basically the same there. There are tons of examples of this in the wild at this point.

ManuallyDrop seems overkill when you have Box::leak().

You may also be able to simply exit() before your allocations get dropped.
Post reply on HN