> Also, unlike Go or Rust, Wuffs’ memory safety is enforced at compile time, not by inserting runtime checks that e.g. the i in a[i] is within bounds or that (x + y) doesn’t overflow a u32.
Am I missing something, or is this statement simply wrong? Have not used Go, but Rust checks its stuff at compile time as far as I know.
Rust does as much as possible at compile time, and some of its most famous features are entirely at compile time, but it will also use runtime checks where appropriate.
Some amount of runtime checking is inherent in any program. Even dependently typed programs must, for example, check input at runtime. They can make it easier to have absolutely minimal runtime checks, however. Rust is adding a limited form of dependent types for this reason.
Functions and types can take integers as monomorphization-time template parameters (const generics).
It would be nice if you could pass (n, t) where n is supplied at runtime, the type of t depends on the value of n, and the compiler only lets you use t if your code is valid for all reachable values of n.
eg. fn(n: usize, arr1: &[i32; n], arr2: &[i32, n]) allowed the function body to assume the two slices can be zipped without losing elements.
Note that i don't have enough experience with either dependent types, zz, or wuffs to explain much further.
> Functions and types can take integers as monomorphization-time template parameters (const generics).
Const generics aren't dependent types though; you're still dealing with known constants at compile time. For it to be dependent types, you need something like in your latter example, where a type is dependent on an actual _value_ passed to a function at runtime.
> eg. fn(n: usize, arr1: &[i32; n], arr2: &[i32, n]) allowed the function body to assume the two slices can be zipped without losing elements.
Often you can wrangle the optimizer into eliminating all but one bounds check by passing slices instead and then slicing one of the inputs in terms of length of the other. The Zip iterator adapter also has optimizations for the case of two slices where iteration will only require two length queries at the beginning and no further bounds checks.
the compiler may tell you, but in general you can add any two `u8` or index an array with any `usize`, the issues will be checked for at runtime (or not at all for the overflow in release mode, by default).
Rust will sometimes have to insert implicit runtime checks. Wuffs appears to require the user to insert explicit runtime checks when needed (it will reject any flow that could possibly overflow, but if you check for overflow it's smart enough to allow that).
Rust does use runtime checks for overflows and will panic if an overflow happens. In cases where you don't care or where it's intentional you can use `overflowing_add()` and its variants.
... in debug builds only, by default. In release, by default, there are no checks, and you get two’s compliment overflow, though the correct thing to do is use those variants, yes.
This was my understanding as well, at least for the majority of memory related tasks.
Edit: actually, it would help if I read the actual quote in your post. I think they’re right in saying that those things are checked at runtime in Rust, though I could be mistaken.
Am I missing something, or is this statement simply wrong? Have not used Go, but Rust checks its stuff at compile time as far as I know.