465 karma · joined February 28, 2014
However, there is one more thing a function can't do: borrowing arguments. Formatting never moves arguments because the format_args! macro generates references to them, which it then creates std::fmt::Argument out of.
(EDIT: nevermind, just looked again and pcwalton was referring to the warning and specifically `Result<(), E>`; oh well)
Because the latter is impossible in Rust and probably more relevant to the usual cited issue with Go's pair approach (i.e. using the null/zeroed `Foo` without checking if there was an error).
I do agree though that the warning isn't to stop you from not handling the error at all, it's more of a hint that maybe you forgot something.
Printing a `Result` may be the legitimate way to handle it in that case, it's largely left to the user to decide what propagates and what doesn't.
Presumably, the `git` URL for dependencies, in `Cargo.toml`, can point to a local git repository. That should let you refer to a branch / commit of that repository, other than the one currently checked out (which is what using a `path` dependency would give you).
But without guarding mutability behind some way to indicate interrupts are disabled, or an outright lock, and without guaranteeing no data races (Sync), it's not actually safe.
And the compiler can't make `static mut` safe to use as any of those requirements could be broken and then it's not safe at all anymore.
`static mut` is not always safe even in a single-threaded environment, because of reentrance - see also https://github.com/rust-lang/rust/issues/53639.
I suspect a lot of the data where you want to use pointers for efficiency, is already in stricter shapes than graphs.
Fully general dependent type would allow passing in runtime values, which is significantly harder to type-check and execute
But as you might be able to tell from their APIs, you still have to allocate stacks somehow.
You are literally describing stackless coroutines. And the generator state transform is that optimization.
If you want to get this without using generators explicitly, it's still stackless coroutines just not how Rust supports stackless coroutines. There was some discussion about making it more implicit but no progress was made in the implicit direction.
Eventually, we might compile everything at once, making the whole "crate" separation a tad bit obsolete (or even silly), other than for code organization (it has other complications, like trait coherence).
What you want is effectively automated proof-search (a hard problem) for an entirely-safe systems language (which doesn't even exist yet, AFAIK. at least not what I described).
Where does C actually gain anything nowadays? Signed integer UB is mostly useful for optimizing misuses of `int` for unsigned values (https://news.ycombinator.com/item?id=17191295), and is evaluation order even relevant anymore? (I don't think clang can pass that information down to LLVM, at all)
The only example I gave which has known drawbacks is the shift one, where modern platforms differ in the behavior, and LLVM will only optimize out the masking of the shift amount on the platforms that have that same behavior in their shift instructions (I think x86, but not ARM).
However, even if you make all of these changes to C, there's still a lot of UB left, in the form of memory accesses, which is much harder to get rid of (see the other comments, some of which mention Rust as well).
Recently I've been trying to imagine what Rust's safe abstractions that use `unsafe` code internally would look like on top of some advanced mix of dependent type theory and proofs about state-manipulating imperative programs.
At that point, the optimizer would have proofs of the invariants it can rely on and it could even potentially emit proofs that every single transformation it performed preserves the (safe) semantics of the code being optimized.
But we're not there yet. Today, we need at least a small subset of the libraries of a systems language to prevent UB "by hand". And in C (or C++, although not necessarily for the same reasons), that's even harder, as there is no notion of a "safe abstraction" (one which you cannot misuse to produce UB).
Both C++ and Rust use pointer ranges for iteration, and Rust even forces indexing/counting to use pointer-sized unsigned integers. So Rust turned off the LLVM bit which says "signed overflow is UB" and did not really lose much from it (AFAIK, anyway).
Lest we forget, any language with a C FFI capability must have some notion of UB, because the FFI effectively includes the C code in its own semantics (unless fully sandboxed, which may be too expensive to be done).
At least the latter claims code compiled once is compatible with all possible hardware configurations, from the start (by way of giving the CPU a "remaining iterations count" and having it reply with how many it can do for the chosen vector lane shapes).
IMO, if it does end up working that well in practice, it does put all of the various incompatible versions of packed SIMD extensions in a pretty awkward spot - could we have skipped all of MMX, SSE, AVX, NEON, etc. versions with technology that has been around for almost half a century?
The value/function conversion is called a "transport" and it actually depends on which equality you've chosen (called "paths" in HoTT).
E.g. bool <-> bit can map false => 0, true => 1 or false => 1, true => 0.
So `(a: bool, b: bool) => a && b` can be "transported" to `(a: bit, b: bit) => a & b` or to `(a: bit, b: bit) => !(!a & !b)` (which is `a | b`).
Of course, bit tricks aren't that useful, but two types which are defined differently (e.g. from different libraries, or different versions of the same library), yet contain the same information, would be interchangeable given a "path" (in the case of the Univalence Axiom, a proof of equivalence).
The other important addition in HoTT is defining non-trivial "paths" (equalities) between values of a type when you define the type - this is used in the HoTT book to describe integer, rational and real numbers, in a way reminiscent of quotient sets.