var hello = try allocator.dupe(u8, "hello world");
allocator.free(hello);
std.debug.print("{s}\n", .{hello});
[0] https://www.scattered-thoughts.net/writing/how-safe-is-zig/ var hello = try allocator.dupe(u8, "hello world");
allocator.free(hello);
std.debug.print("{s}\n", .{hello});
[0] https://www.scattered-thoughts.net/writing/how-safe-is-zig/Zig doesn't have separate sublanguages for macros and generics, instead these are all handled with regular 'comptime' Zig (along with type reflection).
Zig has builtin syntax sugar for dealing with optionals and error unions, which makes code less noisy (although that may be subjective - and I think Rust also got some of that over time).
Zig's interaction with the C world is excellent, and to a lesser degree also to the C++ and ObjC world (the Zig compiler can compile C++ and ObjC code, but still requires a C shim to sit between Zig and C++ or ObjC code).
Zig has much less influence from high level functional languages and less "type system wankery" compared to Rust, this again is a good thing for some people, while disliked by others.
A minor detail, but which I think demonstrates the Zig philosophy: the Zig compiler executable is also the build system and package manager. In Rust this is delegated to a separate tool (cargo).
...and my personal opinion: Zig is a lot more 'elegant' than Rust which results in more joy when writing Zig code. Rust feels entirely too much like 'designed by committee'. With Zig I "grokked" the language in a weekend of tinkering, while with Rust I never really had "fun" despite several attempts of trying to like it.
TL;DR: If I had to create an absolutely waterproof and critical isolated piece of code, I would probably write that in a "simple Rust subset". For anything else I would definitely prefer Zig.
I've tried a couple of times now to get my head around Rust with small efforts. Apparently I need big efforts and deep immersion. "A half-hour to learn Rust"[1] certainly helped with understanding the syntax around lifetimes. I keep reading that understanding the borrow-checker and lifetimes should be a snap for C/C++ programmers. Well, I've been doing C++ for over 30 years and C for longer, and Rust just strikes me as a bondage and discipline language.
[1] https://fasterthanli.me/articles/a-half-hour-to-learn-rust
I use zig because it's enjoyable to use. I hate writing rust, it feels like the language is actively fighting me. I started writing zig, and started loving the process of simplify writing code again. Zig is nice because, to steal their quote. I'm able to spend my time debugging my code, and not debugging my understanding of the language. I've stepped on a use after free but because it's so easy to write and use tests, I found it and fixed it in tests.
Bugs are undesirable, but I can write just as many bugs in rust. So I'm always so confused about why rust users are so panicked about use after free, or other memory bugs. Like somehow the only bugs rust has a handle on is the worst class of bug? Such an odd idea I can't figure out...
And truth be told, if we get liquid types or dependent types in Rust, even those runtime out of bounds checks would be caught at compile-time too.
For something to die at 3am in production, that means either someone pushed code way too late, or something changed in the stack unpredictability. Your compiler wouldn't have been able to predict either of those cases. But bugs are bugs, if I'm called at 3am to fix a bug, I don't want to spend hours trying to fight my compiler, and then fall back asleep while I'm waiting for it to compile. I want to quickly identify the issue, and then to be able to write the smallest possible patch until morning. In my limited experience, rust is conducive to neither of those options.
And you said "if we get liquid types" I assume that means you meant it to say if rust gets? Unless you meant to identify as part of the rust language?
Compile-time checks help you write better code, generally speaking.
Rust itself demonstrates that compile time checks sometimes result in worse code, as many people have found especially when they need to do things rust doesn’t like for performance reasons.
For sure, some checks are unambiguously good. But there is also definitely a moving target of a line towards whether or not more checks has diminishing returns if not negative returns on your code. That moving line will depend on a lot of things.
You also need to be careful because “more checks” doesn’t mean “less work” as you’ve asserted. It means more work in a lot of safe pointer scenarios in rust, and more work actually does often translate to more logic bugs and more complexity when anything changes.
As far as I can tell, more checks doesn’t same any time, it often just changes where your time is spent.
If the ownership structure that gives the best performance for your problem is not tree-like, you will run into issues with the borrow checker. There are ways to tackle it: you could keep the non-tree-like structure and use reference counting and RefCells to move the borrow checking to runtime, or its possible you could use a less performant but tree-like structure that the borrow checker accepts, but both of these examples would come at a performance cost.
It's kinda hard to be more specific because it's heavily dependent on the exact problem.
I don't think those are the only two scenarios. I think the most likely scenario by far is user input triggering a latent issue in a code path and promptly bringing the application down. A classic example, of course, is a missing bounds check.
Modern language design makes this less likely by enforcing invariants, adding checks, and making these kinds of issues impossible by construction. It doesn't mean that there isn't a space for languages without these safe guards, but it does mean you should have a really good reason to use them.
> But bugs are bugs, if I'm called at 3am to fix a bug, I don't want to spend hours trying to fight my compiler, and then fall back asleep while I'm waiting for it to compile. I want to quickly identify the issue, and then to be able to write the smallest possible patch until morning. In my limited experience, rust is conducive to neither of those options.
In my experience with Rust specifically, I've actually found it easier to make those kinds of fixes as the area of code I have to reason about is much smaller. I don't have to worry that a local change will have global effects, as I can much more clearly see the dataflow. It's not a silver bullet, but I've felt much more comfortable doing emergency fixes on Rust than on C++.
‘{company,Github,bug/cve -tracker} says (following code review | testing)* that 70% of reported bugs/exploits are due to memory management failures.’
I write it thusly, because I don’t actual know or remember all the various permutations I’ve seen. But a few that I can recall being mentioned would be Microsoft and Google, regarding Windows and Chrome (or it could have been the entire Google production code base). But other than these types of statements I can’t recall any support for focusing on memory vs other error kinds. This line of argument has led to developers who class memory ‘unsafe’ languages as objective moral bads (and the development of such to be inherently morally bad) [I was literally told this on HN, in the furor over the announcement of Hare Lang]. I think it’s reactions like this that lead to your usage of the word panicked, but I always try to assume the majority of developers are just using a language they enjoy and getting on with the business of making stuff.
Just as a parting note: I’m not attempting to disagree with the statements by these companies, or with the general premise that memory safety errors result in problems for users of software. I’m slightly ambivalent to the whole thing.
— edit for spelling
With that said, I think proper tooling can solve much of the problems with unsafe memory languages. Question is then, why doesn't it happen? Are developers uninformed, arrogant or just lazy? Is forcing it into the language the only way to solve this?
I vaguely recall that 70% of *certain classes* of reported security flaws were buffer misuses (which zig handles quite nicely in releasesafe and using slices, which is recommended)
Edit:
Found it:
70% of security bugs were counted as safe buffer related. Note that by sticking to slices and releaseSafe, zig provides most of the same guarantees as rust in this arena.
It’s unclear whether a buffer issue automatically classified a bug as a security flaw as well, as in many cases it’s simply not. For example, a memory security flaw was reported in Stockfish that was simply not a flaw.
We are also discussing the Microsoft OS, which is a whole different beast than applications.
Overall, meh.
https://www.scattered-thoughts.net/writing/assorted-thoughts...
You could probably make a reasonable analogy that Zig:Rust::Go:OCaml but this isn't perfect. Zig is simpler to work with but it doesn't have as many powerful features, like certain Rust safety guarantees, as you mentioned.
comptime is easily one of the most powerful language features of any language in existence. Comptime gives you literally everything you get from macros and generics without the complexity of needing to know 3 separate Turing complete languages to get it.
Zig doesn’t need a horde of random highly complex differently rule bounded features all complexily interacting with one another because it has 1 easy to use feature that eliminates those needs.
Disclaimer: I'm tentatively exploring this via macros.
comptime doesn't allow you to create domain specific macros like html! in Rust, it also doesn't allow you to constrain what types are accepted by your functions. It also has its own problems with long compile times.
It does, if by constrain you mean “succeed or fail”: just write a comptime if statement and @compileError. If you mean a mechanism like concepts or SFINAE then you are correct.
To check that an anytype meets the decls of SeekableStream is rudimentary.
It’s funny that you say that about html, as a project in the discord was just released to get comptime html only in the last few weeks. I don’t remember the project name unfortunately.
Sure it does!
Zig's string format function is written using comptime. It parses the format string and validates the arguments at compile time. See https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig
I also toyed around with turning a math equation dsl into compiled statements at https://github.com/Laremere/alg I got matrix math working so, eg, if you multiply a 2by1 matrix by a 1by2 matrix, it returns a matrix which is typed to 2x2.
Yep, this is probably why I like it so much. I liked OCaml when I used it and Rust derives a lot of features from OCaml, as well as its initial compiler being written in OCaml, so I think the creators saw how well parts of OCaml fit together (Option and Result types, ADTs, etc) and added it to Rust, as Rust was much different than currently [0].
Zig is “a better C”.
Understanding that makes it clear where Zig shines and Rust is less ergonomic.
Both are good, but I see them as complementary and not really adversarial or in competition.
Rust is safer, better overall + ergonomics, ecosystem, etc.
However, Zig have some nice properties:
- Is easier to do bit/memory wrangling in Zig than in Rust
- Is FAR easier to cross-compile. To the point that for example I use Zig to compile my Rust project for musl
- Is MUCH faster to compile
- Is far "smaller" that is nice in constrained situation (not just "environments", for example, you can use zig for when you will use gcc/llvm to compile some small c file)
Zig is a very good match if the scope of the project is relatively small. Rust shine much more when is more big or you need to work for a team of people.
Also, if you plan to make a library that will be used by others, better to use a safer lang.
I don't see both as competitors. They are excellent complements, and my dream is to Zig to replace C and Rust/ada/pascal to replace C/C++.
P.D: A probably good match is to make some low-level thing in Zig (where before was C) and FFI it in Rust.