HN user

tatterdemalion

2,029 karma

https://github.com/withoutboats

Posts3
Comments400
View on HN

The same code with `await` :)

    let (reader, vec) = await { io.read_until(reader, b'\n', Vec::new() }?;
    if vec.len() == 0 {
        return Err(Error::new(ErrorKind::BrokenPipe, "broken pipe"));
    }
    let string = String::from_utf8(vec)?;

The user specifically asked about stackfull coroutines, which is not the direction we're going. But I think the pain point the user talked about (futures right now are difficult to deal with) will be resolved by our stackless coroutine approach, and more in line with Rust's values around "zero cost abstractions."

The RFC supports it, eddyb's list of milestones in the compiler just don't get you all the way there.

Signal Foundation 8 years ago

Signal's use of SGX was to increase users' trust in Signal - OpenWhisperSystems couldn't log your contacts even if they wanted to. Its up to potential users to decide if they think OpenWhisperSystems will surreptitiously perform an attack on their own secure enclave to secretly log your contact information.

My apologies if I misunderstood you - I read your comment as suggesting that safe abstractions are "leaky" and therefore create additional responsibilities for users to validate that they are using them safely when composing them together. This is not the case unless those abstractions are incorrect - which is the same situation you are with any language, just most languages those abstractions exist within the language runtime & not in libraries.

Proving the correctness of unsafe code is totally different from what you talked about, which was composing different abstractions with unsafe internals together.

Users of safe Rust do not need to worry about whether the composition of two safe interfaces that use unsafe internally is safe unless one of those interfaces is incorrect. Your comment would suggest that users need to think about the untyped invariants of each library they use, but this is not correct, libraries are not allowed to rely on untyped invariants for the correctness of their safe APIs.

There is a conflation happening here. What is the nature of this bug when you compose these two libraries together?

If it is a violation of Rust's safety guarantees, then at least one of those libraries has a bug, it is exposes a safe abstraction which is not actually safe. One could not argue that the safe Rust program is wrong; the library exposing an unsafe interface as safe is unarguably wrong.

If the library just behaves incorrectly in a manner disconnected from the type system because some global state was changed in a way it doesn't expect ("the hardware" in this case), then that's a normal bug & it is not connected to unsafe code at all.

The major difficulty here is that in general, unsafe pieces of code cannot be safely composed, even if the unsafe pieces of code are individually safe. This allows you to bypass runtime safety checks without unsafe code just by composing "safe" modules that internally use unsafe code in their implementation.

This comment suggests you don't have much domain knowledge about how `unsafe` in Rust works, so I'm surprised you speak with such confidence. Your comment is flatly wrong: users using only safe code are not responsibility for guaranteeing the composed safety of the components they use (whether or not they are implemented with unsafe code).

Interfaces marked safe must uphold Rust's safety guarantees, or they are incorrect. They are just wrong if they have additional untyped invariants that need to be maintained to guarantee their safety; interfaces like this must be marked `unsafe`.

Because they cannot depend on untyped invariants, any correct implementation with a safe interface can be composed with any other. This ability to create safe abstractions over unsafe code which extend the reasoning ability of the type system is a fundamental value proposition of Rust.

- We are directing material resources (as in, hours of paid work) to improving compiler performance. We consider this a serious problem, and "the compiler performance is always improving" is not an accurate gloss of the amount of work people are putting into solving it.

- We would love to have a REPL but there are nontrivial technical challenges, and it has not been a major requested feature by our users.

- I have never heard the complaint about the size of packages before now; I have no idea how we compare to other languages in this regard.

I've witnessed some of the conversations you're talking about (I think I engaged with you about REPLs in the past), and I think the "true-believer syndrome" has more to do with how you interpret the answers you receive than with the behavior of anyone else.

What we could do better than we do today - and your comment about ergonomics alludes to our work to improve this - is ease the onboarding of that complexity we have to be "honest" about. Its a design constraint of Rust that it must maximize user control, but that does not imply that users have to be faced with all of those choices as soon as they first try to write Rust. In some respects I think this article is an attempt at a counterargument to that work (suggesting that it is "dishonest") but I fundamentally do not believe that there is a contradiction between giving advanced users control and making it easier for new users to write correct code before they have a full understanding of the entire system.

This is not my experience using Rust. I had six months of programming experience when I first tried Rust. I definitely got confusing errors at first, but I grasped the system within a month or two. And that was in 2014 - the borrowchecker and especially its errors have way improved since then!

But this is also a very different objection from your initial objection, which was just a statement of fact about type systems for turing complete languages.

Do you stay away from all type systems? This would be an accurate negative framing for any type system, which all necessarily reject some correct programs.

There's another reason Rust's ecosystem uses futures (as opposed to having some library-based greenthreading system): each future is like the stack of a userspace thread, but perfectly sized (it will be as large as the largest stack space needed at a yield point). This reduces the memory footprint of services using futures.

What's missing here is that Go has a userspace scheduler and greenthreads. Despite presenting the user with something that looks like old school blocking IO, because you are only blocking a greenthread it is actually asynchronous IO under the hood.

And of course there is a huge amount of abstraction to build this up. It's just all hidden in the language runtime, so people can say with a straight face that there is "no abstraction and no cruft."

There are various criticisms of tokio, coming from different directions. Some have to do with the fact that some abstractions in the futures ecosystem are leaky today and that makes them less easy to use than they could be (though they won't always be leaky[]). But others have to do with understanding the internal implementation of these abstractions - people who feel they must understand how their library works internally before using it.

Of course, schedulers are just complicated. Most of the time you don't think about how complicated your scheduler is, because its either an OS primitive in the kernel or a language primitive in your language's runtime. But since tokio is a library - and modular - it gets criticism for being complex that in my opinion is unfair.

[] To be more concrete: a future is essentially a state machine representing the stack state at any yield point; it can't (currently) contain lightweight references into itself because they'd be invalidated when the future is move around. This means using borrowing in futures programs is often infeasible today. Solutions are in the works.

I think this was a very bad move, because Mozilla installed adware in all of its browsers. The fact that it was installed through an add on, though, seems irrelevant. Mozilla developers can distribute arbitrary code to all users because they write the browser. The add on just makes this particular bit of code user visible.

Similarly, this automobile is well built despite having every design decision be wrong. There's some distinction between the architecture and the craftmanship - a poorly crafted and poorly architected anything is just a pile of rubble, but a well crafted thing with poor architecture is fascinatingly bizarre.

The PARTNER study studied 767 serodiscordant couples, with the HIV positive partner undetectable. All participating couples did not use condoms at least some of the time. They found zero cases in which the undetectable partner infected the negative partner.

http://www.aidsmap.com/No-one-with-an-undetectable-viral-loa...

Frequent testing and early intervention are very effective to achieving an undetectable viral load. The adoption of these as public health policy, as well as making available of prophylactic measures for at risk populations, has been effective at combating the spread of HIV - e.g. in San Francisco, where infection rate has been declining significantly over the last 5 or so years.

Having a compiler and standard library written in the language that it compiles has some huge benefits for increasing the pool of possible contributors.

But this isn't the official compiler, this is someone's personal project?

It took me a bit to figure out what you could be talking about. Rust does not insert runtime checks like this of anything by default except for boundschecking on indexing operation.

You must mean that you stored your `CounterWidget` in a RefCell, which does have the semantics that if you mutate it while holding a mutable reference to it, it will crash. But your code sample didn't include a RefCell at all, so this is very unclear.

Yes, its true - Rust provides a hard guarantee against simultaneous mutable access to the same data. This is invaluable even in a non-concurrent context, both because of memory issues like iterator invalidation & higher level logic errors that result from silent mutation at a distance.

I can assume looking at your code that didClick() doesn't mutate this CounterWidget - or even access it. This means I know that if I rearrange the counter increment with the didClick callback, I know nothing is semantically different. I can introduce additional code to the function, knowing that the state is the same after didClick as before it. All of this information is very useful to me when I'm trying to maintain this codebase (not to mention useful to the optimizer).

If you want to be able to provide mutable access to the callback, just make it an `fn(&mut CounterWidget)`.

Except that normally a new feature does not involve creating a new system. Technical debt significantly impacts velocity of implementing new features in the future.

This is also how HashSet is implemented in Rust (internally its a HashMap<T, ()>, and zero sized structs are optimized out just like Go).

But by providing a set type, Rust is able to provide for users various set operations which Go requires you to write by hand. It becomes hard to argue that a set is simpler than a slice when you have to write your own methods dealing with union, disjunction, intersection, difference, subsets/supersets, etc every time.

The claim that Rust's generics system is "fancy template cruft" on top of "all the complexity of C++" is absurd, though.