1,476 karma · joined December 1, 2014
But it's less so true in the sense that the two teams had different use cases, and simply took divergent paths. Rust took the "easier"* path that limited expressivity in favour of a simple execution model that requires a minimal runtime, which in turn enabled them to focus on more interesting static analyses. Swift took the other path.
* This may sound like a slight against Rust, but both the Swift and Rust folks who worked on this stuff agree that what Swift did is a comically huge amount of work in pursuit of a relatively niche use case (idiomatic-feeling system apis). There's only so much time in the world, and Rust spent its time on other problems.
The font settings are just from a copy of bootstrap from like 6 years ago, because I have absolutely no eye for this sort of thing.
Same reason I prefer to use imperfect terms that capture the important aspects of the problem-space from an english-speaking perspective. Are ligatures the right word for how arabic and marathi get shaped into glyphs? Maybe not, but as long as you get that the æ ligature can be synthesized from ae by a font, and that this is super important for some languages, you're on the right path.
I don't even know what the fragments I use mean, lol. I like to assume I'm just copy-pasting Arabic swears around. Apparently at least one is just Manish's name?
Swift has taken the observation further and is investigating ways to avoid ever having a immutable+shared-reference-to-primitive vs primitive distinction in the language, while still introducing this distinction for types where it is interesting. (e.g. reference counted classes or atomics)
webrender is "the stuff that works pretty ok and we can run through CI". In principle enabling webrender should get you the ~same results as not having it on, visually, modulo a few known bugs.
webrendest is "everything we're working on, no matter how broken"
It's a pretty big project so there's lots of stuff in tree that's barely functional (especially true with the layers-free pivot).
* All unsafe operations don't exist.
* All unsafe operations exist, but the literal unsafe keyword and its machinery doesn't exist
The latter is how most ostensibly safe languages work. See Haskell's UnsafePerformIO, Swift's UnsafePointer, and Java's JNI for 3 examples off the top of my head.
The former is just a really gimped language that would have been a pain in the neck to implement libraries for (see other replies for examples).
Note that we sort of made a "new" kind of unsafe with the UnwindSafe trait: https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html
That's probably how we intend to solve these kinds of problem in the future.
Re: aliasing -- if it's a serious enough problem, one of two things will happen:
* Someone will develop a version of asan/ubsan for Rust.
* The Rust devs will be forced to reduce the extent to which they apply alias analysis by default (possibly with a flag to opt into it). At least temporarily.
The rust devs have backed off optimizations in the past when they break stuff in the ecosystem (struct layout optimization). But they also work with the affected devs to fix those bugs so they can turn the optimization on.
Meanwhile mapping in Rust generally takes an iterator and produces another iterator that will apply the given closure to the current element as it's yielded. So the naive codegen for iter().map().map().map().collect() is exactly what list fusion is trying to produce -- no temporary lists.
TL;DR: making "map" having the monadic `T[U] -> T[V]` signature is really expensive. ¯\_(ツ)_/¯
Maybe something that leverages signed overflow, overlong shifts, or type punning.
But if you have enough control over your codebase to mandate the compiler/flags its built with, then you can generally tell the major C compilers to act like Rust in these cases.
That said, the expected win for Rust over C(++) in practice is that you can be more "reckless", because you have a stronger type system protecting you from messing things up. A production-quality C(++) codebase might rightly do more copies, use more reference counting, and use less concurrency just because the risk of doing otherwise isn't worth the potential performance wins.
Organizations have limited resources to commit to optimizing/verifying code. Rust is intended to get you more bang for your buck.
I agree the system you propose would be simpler in terms of spec and effort, but I don't know about simpler to use. Much like removing mutable references in favour of `foo(X) -> X` would be a simpler type system, but awful to use compared to `foo(&mut X)`.
I also make it very clear that the implementation is mostly free. It's just using tools we already have with minor tweaks. Most of my issues are exactly the pragmatic matters: your standard library isn't built to handle it, nothing in the ecosystem is built to handle it, and it everyone has to opt into support for backwards compatibility reasons.
* Collision attack: find X and Y such that hash(X) = hash(Y)
* Second-preimage: given X, find Y such that hash(X) = hash(Y)
* First-preimage: given hash(X), find Y such that hash(X) = hash(Y)
> If you have an encrypted message that you hashed/signed _before_ encrypting, and Eve wants to know what you said, first-preimage would be worse, and second-preimage wouldn't buy you anything.
first-preimage doesn't do anything here. It gets you some text that matches the hash. It's overwhelmingly unlikely to be the original message, and if it's not the exact original message, it won't have any similarity to it. Unless you can enumerate all hash collisions for a value efficiently, which is a much a stronger claim, this isn't any better than brute-force guessing the text.
> Getting the preimage of the document and hashing that preimage just gives you back the original hash---it's like an identity function. It doesn't give you a second document.
The second-preimage attack supposes you have X, the first-preimage attack supposes you have hash(X). If "all" you have is a first-preimage attack, then it's trivial convert it into a second-preimage attack. You hash(X) and feed it into your attack.
Boring single-threaded swift code can currently be incredibly slow for no clear reason; this is an attempt to fix that.(addressors++)
The details this manifesto covers need to be settled for ABI-stability, which is still one of the primary goals for Swift. (because it enables making it a part of Apple's OSes)
Although this system proposes dynamic checking for these cases, it's explicitly not atomic checking. Atomic checking would be very expensive even though most code doesn't need it. Also there's a general sense that java, which does atomically guard all shared mutable state, has the problem that these accesses are defined but ultimately useless. Part of this is that coherent updates require coarser locks.
Personally, as someone who has spent a lot of time trying to manage the safe/unsafe code boundary, I'm inclined to agree with you. Unsafe code in Swift has an even scarrier subtle hazard: if the last uses of a value are through an unmanaged handle, the compiler won't know and can free the managed value before those uses. You need to insert a marker saying "please keep this alive". Even worse: returning a value, after inlining, isn't sufficient to establish a use!
That said, your lock handle example doesn't matter in swift today: you really want a lock handle to be a struct, and structs can't have destructors. You also want it to have move semantics, which don't exist yet. This proposal suggests moveonly values that could adopt destructors (like Rust), and if you're already adding an annotation for destructors, it's not out of the question to add another one asking for "true" lexical destructors.
Normally this wouldn't matter, but Rust also supports compiler plugins written in Rust. These link directly to the compiler that compiles them. With the stage 1 compiler's quirky ABI, the plugins it compiles might not be able to interact with it.
The stage 2 compiler, on the other hand, uses the same set of ifdefs that the stage 1 compiler has. So it was built by a compiler with the same output-ABI as itself, and so plugins have the same ABI.
So:
* snapshot: Built with snapshot ABI, produces snapshot ABI (snapshots are old stage2 compilers)
* stage0-output: built with snapshot ABI, emits stage0 ABI.
* stage1-output: built with stage0 ABI, emits not(stage0) ABI.
* stage2-output: built with not(stage0) ABI, emits not(stage0) ABI.
https://github.com/brson/annotated-std-rs/commit/e50c2b16455...