Earlier quoted context omitted.
It's an open question [1] along with other safety checks both comptime and runtime [2]. The issue here is a balance of keeping language complexity low while providing safety at the same time and not depriving users of control when they know better than the type checker/lifetime analysis. [1]: https://github.com/ziglang/zig/issues/782 [2]: https://github.com/ziglang/zig/issues/2301
This is such a squishy value proposition, which is why people aren't taking Zig seriously. Rust's value proposition is simple: no GC and no undefined behavior. Period. Nothing else has that.
Failing to Learn Zig via Advent of Code
281–290 of 338 posts
Re: Failing to Learn Zig via Advent of Code
#282Earlier quoted context omitted.
I feel like Nim is a much closer comparison than Rust. All three could be described as "grassroots projects on a holy mission to replace C and/or C++," but Nim and Zig are more similar in terms of operating budget/team size/adoption rate. I may be a bit biased, but many of the problems mentioned in the post are the sorts of things I haven't dealt with in Nim for probably 2 years. Nim's error messages are still hit-or…
Rust isn't on a holy mission; it's simply the only memory-safe language without a garbage collector. It's really that simple. There is no international conspiracy behind Rust's popularity. It's the only thing that can do what it does in that respect.
Re: Failing to Learn Zig via Advent of Code
#283Earlier quoted context omitted.
There is nothing fundamental about ownership-based resource management, memory-safety can be achieved in other ways. Zig is C, it's not meant to abstract away memory management. You can do whatever in Zig, write a specialized allocator that's verified to be correct, even for a microcontroller with 2 KiB memory. Zig is a machine-oriented language, which means no hidden control flow, no hidden allocation. You can write…
> nothing fundamental about ownership-based resource management, memory-safety can be achieved in other ways. Not other way s . One other way. Just one. Garbage collection.
Re: Failing to Learn Zig via Advent of Code
#284Earlier quoted context omitted.
I still can't figure out how Zig proposes to prevent undefined behavior without a borrow checker (aka MLKit regions) or GC. AFAICT the answer is "inject as many runtime checks as needed" although the docs seem to go way out of their way to avoid making this explicit.... or deal with the fact that these checks are now runtime failures rather than compile-time failures, and therefore need code to handle them. It seems…
Zig doesn't propose to prevent UB (in fact the docs say that it takes aggressive advantage of it for optimization). (Neither does Rust.)
Your unsafe Rust is supposed to provide suitable constraint/ guarantees that you, the programmer, conclude it does not have any Undefined Behaviour. The language can't force you to do this, and at some point it becomes a social contract not a programming language feature.
I wrote the misfortunate crate to explore Rust's promise here. The crate provides legal but obviously inappropriate implementations of lots of safe Rust traits, and sure enough nothing blows up, there is no undefined behaviour.
The defined behaviour can be undesirable for example if you insist on putting a bunch of misfortunate::Maxwells in a HashSet you're going to have a bad time. Rust doesn't promise this is a good idea, it might cause infinite loops, memory leaks, all sorts of defined trouble, but it won't be Undefined Behaviour.
Re: Failing to Learn Zig via Advent of Code
#285Earlier quoted context omitted.
Zig doesn't propose to prevent UB (in fact the docs say that it takes aggressive advantage of it for optimization). (Neither does Rust.)
safe Rust doesn't have any Undefined Behaviour. Your unsafe Rust is supposed to provide suitable constraint/ guarantees that you, the programmer, conclude it does not have any Undefined Behaviour. The language can't force you to do this, and at some point it becomes a social contract not a programming language feature. I wrote the misfortunate crate to explore Rust's promise here. The crate provides legal but obvious…
Re: Failing to Learn Zig via Advent of Code
#286Earlier quoted context omitted.
Rust's purpose, its whole reason to exist, is to displace C. Rust will unavoidably fail in that, because anybody still using C is not willing to learn anything else: anybody willing to move on from C already did, long ago. Rust is already approaching C++ in complexity, surpassing it in some places; and also in expressive power, but not surpassing it anywhere yet. If Rust does not end up fizzling (which is still very…
Even if the only success story for Rust would be mainstream adoption of lifetime checkers across languages to some extent, that would already be a victory as it managed to change the baseline of language design across the industry. A subject that now has become even regular presence at C++ conferences and considered a must have in static analysers roadmap by all major vendors. Rust might fizzle out in a decade, and s…
Re: Failing to Learn Zig via Advent of Code
#287Re: Failing to Learn Zig via Advent of Code
#288Earlier quoted context omitted.
> `a+b` will have the same value as `b+a` No, as IEEE754 doesn't even guarantee that `a + b` == `a + b`. You always need to compare the absolute difference of two values against an ε. So a == b |a - b| But for sensible comparison of floats, the addition is commutative.
> No, as IEEE754 doesn't even guarantee that `a + b` == `a + b` What? I'm pretty sure that is plain incorrect. Can you back that claim up with references?
See for example:
int main() {
double q;
q = 3.0/7.0;
if (q == 3.0/7.0) printf("Equal\n");
else printf("Not Equal\n");
return 0;
}
On an extended-based system, even though the expression 3.0/7.0 has
type double, the quotient will be computed in a register in extended
double format, and thus in the default mode, it will be rounded to
extended double precision. When the resulting value is assigned to the
variable q, however, it may then be stored in memory, and since q is
declared double, the value will be rounded to double precision. In the
next line, the expression 3.0/7.0 may again be evaluated in extended
precision yielding a result that differs from the double precision
value stored in q, causing the program to print "Not Equal". Of course,
other outcomes are possible, too: the compiler could decide to store
and thus round the value of the expression 3.0/7.0 in the second line
before comparing it with q, or it could keep q in a register in
extended precision without storing it. An optimizing compiler might
evaluate the expression 3.0/7.0 at compile time, perhaps in double
precision or perhaps in extended double precision. (With one x86
compiler, the program prints "Equal" when compiled with optimization
and "Not Equal" when compiled for debugging.) Finally, some compilers
for extended-based systems automatically change the rounding precision
mode to cause operations producing results in registers to round those
results to single or double precision, albeit possibly with a wider
range. Thus, on these systems, we can't predict the behavior of the
program simply by reading its source code and applying a basic
understanding of IEEE 754 arithmetic.
The relevant conclusion: Neither can we accuse the hardware or the compiler of failing to
provide an IEEE 754 compliant environment; the hardware has delivered a correctly rounded result to
each destination, as it is required to do, and the compiler has
assigned some intermediate results to destinations that are beyond the
user's control, as it is allowed to do.
https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/b...Re: Failing to Learn Zig via Advent of Code
#289Earlier quoted context omitted.
I still can't figure out how Zig proposes to prevent undefined behavior without a borrow checker (aka MLKit regions) or GC. AFAICT the answer is "inject as many runtime checks as needed" although the docs seem to go way out of their way to avoid making this explicit.... or deal with the fact that these checks are now runtime failures rather than compile-time failures, and therefore need code to handle them. It seems…
You're conflating undefined behavior with spatial/temporal memory safety, the latter of which is what is 100% prevented by Rust's borrow checker, provided you're not interfacing with hardware, in which case I believe that things change more in binary fashion. However, Zig treats memory safety not as an extreme-at-all-costs but as a spectrum (there are reasons for wanting to think like this, at least when writing low-…
Suppose we have been given a 32KB data structure with some "step" bytes - in a conforming input these should always sum to less then 32768 and thus the total will easily fit in a 16-bit unsigned integer, so that's what our naive program does. Unfortunately attackers provided a structure whose step bytes sum to more than 65535...
Zig will panic here if using default arithmetic with default release builds. If the attacker wanted to cause a Denial of Service, job done already.
Rust will panic if explicitly told to enable checked arithmetic on release but it also provides explicit checked, wrapping, saturating and so on variants of the arithmetic operators if you want them for this part of your software (perhaps anticipating the risk) you can just have that without touching the behaviour of all other arithmetic in the program. 65530u16.checked_add(255u16) is None even in a default release build of Rust, what you do with that None (silently abandon this input? log the error?) is up to you and of course may not be adequately tested.
However, in WUFFS we simply can't write the erroneous program. It doesn't compile because WUFFS can't see why it's safe. Because it isn't safe. WUFFS requires the programmer to spell out what's going on, and so either you have to realise what might happen ("Oh, it can overflow, I should handle that") or choose a strategy that can't suffer the problem, ("Let's not sum up those steps, I see a different way to handle valid input").
Re: Failing to Learn Zig via Advent of Code
#290Earlier quoted context omitted.
Even if the only success story for Rust would be mainstream adoption of lifetime checkers across languages to some extent, that would already be a victory as it managed to change the baseline of language design across the industry. A subject that now has become even regular presence at C++ conferences and considered a must have in static analysers roadmap by all major vendors. Rust might fizzle out in a decade, and s…
I have not heard of a lifetime checker in any other language, except maybe Midori.
Chapel, HPC language mostly sponsored by Intel and HPC
D programming language,
https://dlang.org/blog/2019/07/15/ownership-and-borrowing-in...
Ada/SPARK,
https://docs.adacore.com/spark2014-docs/html/ug/en/source/la...
Swift,
https://github.com/apple/swift/blob/main/docs/OwnershipManif...
ParaSail
Project Verona from Microsoft Research
https://www.microsoft.com/en-us/research/project/project-ver...
Project Snowflake from Microsoft Research
https://www.microsoft.com/en-us/research/publication/project...
And finally your favourite C++
"Implementing the C++ Core Guidelines’ Lifetime Safety Profile in Clang"
https://llvm.org/devmtg/2019-04/slides/TechTalk-Horvath-Impl...
Also the "Clang Static Analyzer - A Tryst with Smart Pointers" talk at 2021 LLVM Developers Meeting.
For the Visual C++ part of the story
https://devblogs.microsoft.com/cppblog/lifetime-profile-upda...
And GCC as well, although they are late to the party
https://gcc.gnu.org/wiki/DavidMalcolm/StaticAnalyzer
Finally a couple of CppCon 2021 talks that touch on the subject in various ways,
Type-and-resource safety in modern C++
Code Analysis++
Static Analysis and Program Safety in C++: Making it Real
Finding Bugs Using Path-Sensitive Static Analysis