Kudos for finishing it. I’ve gone on a similar quest with https://mk12.github.io/sicp, but I’m still on chapter 3. I keep getting distracted by new ways of improving the website rather than making progress in the book.
This is another big difference between Rust and Zig. Rust lets you have it both ways with configuration. Zig places much more value on being able to read and understand any Zig code in the wild, based only on “it compiles”. Rust’s “it compiles” gives you lots of information about safety (modulo unsafe blocks), but very little about certain other things until you’ve examined the 4-5 places which might be tweaking configuration (#[attributes], various toml files, environment variables, command line flags).
If you allow shadowing, then you rule out the possibility of the value being used later. This prevents accidental use (later on, in a location you didn't intend to use it) and helps readability by reducing the number of variables you must keep track of at once.
If you ban shadowing, then you rule out the possibility of the same name referring to different things in the same scope. This prevents accidental use (of the wrong value, because you were confused about which one the name referred to) and helps readability by making it easier to immediately tell what names refer to.
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.
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.
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
I think it's worth pointing out that the example in the article contains a bug caused by not having shadowing: "const foo3 = try foo.addFeatureB();" should not be using the original foo, but foo2.
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 a valid value of type T.
When creating a mutable reference, then while this reference exists, the memory it points to must not get accessed (read or written) through any other pointer or reference not derived from this reference.
When creating a shared reference, then while this reference exists, the memory it points to must not get mutated (except inside UnsafeCell).
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.
> you can’t have references to uninitialized memory.
The method is available[0] in nightly Rust
pub const unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>>
where
T: Sized,
{
// SAFETY: the caller must guarantee that `self` meets all the
// requirements for a reference.
if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit<T>) }) }
}
This is mostly a concern with the Rust stdlib, though. And it's in principle fixable, by writing new varieties of those stdlib functions that take raw-pointer or &UnsafeCell<...> arguments, and delegating the "safe" varieties to those.
There's compiler-level traits like `Iterator` and `Future` which enforce references. If wanting to do intrusive pointers into them, one risks creating overlapping references: https://github.com/tokio-rs/tokio/issues/3399
References to UnsafeCell<> should still be safe, because the only thing you can do with an &UnsafeCell<T> is extract a possibly-mutating raw pointer to T. (UnsafeCell<> is also special in that, like Cell<>, you can mutate through it without incurring UB.)
I host my site on NearlyFreeSpeech for about 40 cents a month and there’s no bandwidth limit. The FAQ has always said: “Currently we are not tracking (and hence not billing for) extra bandwidth usage. This could change in the future, but currently we have no such plans.” Even though I could host with Cloudflare for $0, I think the tiny savings are not worth imposing the captcha on people.
The problem is not that + calls a function. The problem is that + could call one of many different functions, i.e. it is overloaded. Zig does not allow overloading plus() based on the argument types. When you see plus(), you know there is exactly one function named “plus” in scope and it calls that.
operator + is overloaded even in plain C: it will generate different instructions for pointers, floats, integers, _Complex, _Atomic and the quasi standard __float128. Sometimes it will even generate function calls.
Not if `plus` is a pointer. Then `plus()` is a conditional branch where the condition can be arbitrarily far away in space (dynamically scoped) and time. That's why I think invisible indirection is a mistake. (C used to require `(*plus)()`.)
FWIW the goal for comptime Zig execution is to be at least as fast as Python. I can’t find it now but I remember Andrew saying this in one of his talks at some point.
I just started a new job where I’m collaborating with the maintainers of mercurial, and was asking them about these alternative VCSs last night. It was interesting to learn that all these people are in contact with each other and there’s a lot of cross pollination of ideas. And for some newer ideas e.g. in jj and pijul, where just reading the website would have me 100% sold, they had more nuanced takes on the pros and cons. So I feel less confident in giving advice than I would have a few days ago. But if you need seamless git compatibility, I’ve personally found git-branchless to be the best solution for the requirements you listed as a thin layer over git. I also tried jj a while back and I liked it but I found being further removed from git made it harder to drop down into regular git when I needed to. In particular I had issues with jj log showing lots of irrelevant commits but that was probably because of our particular Gerrit setup and might have been fixed by now.
I find it weird how so much generation discussion seems to skip gen x. I see references to boomer, millennial, and zoomer/gen z way way more often than gen x.
Gen X’s arc was going from Reality Bites/Wayne’s World slacker grunge culture into the stultifying white collar Office Space of the Matrix and then disappearing from the zeitgeist entirely after 9/11 made Fight Club’s ending sort of real.
reply