Live data from Hacker News

Rust means never having to close a socket

blog.skylight.io

61–70 of 165 posts

Re: Rust means never having to close a socket

#61
post #42
post #9

Earlier quoted context omitted.

> Does a drop somehow say something to the type system as well, disallowing reads and writes afterwards? Yes.

I'm curious about the drop implementation, which I found here ( http://doc.rust-lang.org/src/core/home/rustbuild/src/rust-bu... ): pub fn drop (_x: T) { } I understand that the drop function is simply taking ownership of the passed value so Rust knows that once "drop" finishes, the _x can be 'dropped'. But I though that the T had to be bound to have "Drop" trait (i.e. T: Drop) so that Rust knows it's possible to inse…

The names are a bit confusing here, and I might petition to change them.

The `drop` here is just a simple library function and isn't technically related to the `Drop` trait, which is implemented with magical compiler pixie dust and allows you to define a destructor via a `.drop` method. Anything that goes out of scope has this `.drop` method called automagically.

You're correct in that if you were calling `x.drop()` explicitly, you would need to have a `Drop` bound on `T`.

Re: Rust means never having to close a socket

#62
"Rust achieves both of these features without runtime costs (garbage collection or reference counting), and without sacrificing safety."

If it is freeing everything at the scope boundary it is incurring a huge runtime cost relative to garbage collected languages. GC scales with the number of live objects during GC while this scheme scales with the number of allocated objects. It is far more predictable than GC but I don't believe that it isn't more expensive.

Re: Rust means never having to close a socket

#63
post #27
post #14

For files python[0] does not need an explicit with to close the file. It gets closed when references reach zero. [0] For the pedants repeating each other in the replies... Where by python, I mean the implementation that 98% of people use, called python, which the competition calls CPython. Yes it is a great feature of the implementation, like the deterministic memory management through reference counting.

What happens if there's a cycle? It will get closed when the cycle is finally collected. You can rely on a GC (and refcounting + a cycle collector is absolutely a GC) to finalize your resources for you, but it is quite difficult to reason about when the file will eventually be closed. Also, the reference counting semantics of CPython are implementation-specific. From the PEP that introduced `with`: > Note that we're…

Props to Rust for tackling this head on, more languages should provide resource allocation that's deterministic, predictable and syntactically convenient.

CPython's behavior is nice, but it seems to me it came about by accident. Big heavy resources use refcounting because everything uses refcounting. Plus, if CPython had true concurrency, across-the-board refcounting probably wouldn't have lasted nearly as long (reason being: multithreaded refcounting requires atomic ops which make them much more expensive).

But in the general language design world, IMHO, refcounting is actually a pretty good compromise for resource management. Because:

1) The efficiency lost compared to Rust's approach is probably immaterial (since the Big Expensive Object that you're mananaging dwarfs the refcounting -- syscalls and file descriptors are expensive). So I don't think zero overhead is critical.

2) Cycles are pretty easy to avoid when you're only refcounting Big Expensive Objects (FDs, mmapped buffers, etc. etc.) For a general runtime, refcounting is tricky. But if it's a special part of the language that's small, simple, and only for resource management, it's pretty convenient. I think it's worlds better than the approach of mixing GC and resource management -- language designers should admit that's a terrible idea.

Re: Rust means never having to close a socket

#64
post #58
post #54

Earlier quoted context omitted.

1. Familiarity to C++ programmers, for whom taking a reference via `&` is a familiar operation. 2. There are two different kinds of references: `&` is immutable and `&mut` is mutable. One way or the other you'd need some sort of differentiation. 3. Borrowing, though crucial to the idea of Rust, is still less fundamental than ownership. You can have a language with ownership and without borrowing (indeed, this is how…

Also, there are a whole host of very common types (integers, etc.) that actually want to be passed by value and copied. There's no point in making a pointer to an integer and then dereferencing the pointer in the callee when an integer fits into the same amount of space as a pointer in the first place. Very simple types (without heap pointers or destructors) can be passed by value and efficiently copied. You end up w…

If something is immutable (and doesn't own something else), it doesn't matter whether it's passed by value or reference. That's a decision the compiler can, and should, make, as an optimization decision. In Modula, where pass-as-read-only was the default, small items were passed by value and large ones by reference. "Small" is a CPU-dependent optimization and should be left to the compiler, not "user intuition".

The default should be pass as read-only reference. If you want a copy of something, you should have to explicitly make one. If you want write access, you should have to say so. Rust has all the right concepts, but the defaults aren't in the safest direction. The experience of "const" in C/C++ teaches us that they should be.

Re: Rust means never having to close a socket

#65

"Rust achieves both of these features without runtime costs (garbage collection or reference counting), and without sacrificing safety." If it is freeing everything at the scope boundary it is incurring a huge runtime cost relative to garbage collected languages. GC scales with the number of live objects during GC while this scheme scales with the number of allocated objects. It is far more predictable than GC but I…

I'm a little bit confused. Every object that gets allocated has to be freed at some point. That cost is the same no matter how the memory is managed.

Are you talking about an additional cost beyond "freeing memory that was allocated"?

Re: Rust means never having to close a socket

#66

"Rust achieves both of these features without runtime costs (garbage collection or reference counting), and without sacrificing safety." If it is freeing everything at the scope boundary it is incurring a huge runtime cost relative to garbage collected languages. GC scales with the number of live objects during GC while this scheme scales with the number of allocated objects. It is far more predictable than GC but I…

> It is far more predictable than GC but I don't believe that it isn't more expensive.

A GC has to determine liveness of objects, so it has to traverse the object graph (or at least the most recent generation if the GC is generational) to know which objects are still alive and which aren't. That's what "GC scaling" refers to. Then it still has to deallocate dead objects, and pay the same deallocation cost as scope-bound free.

Re: Rust means never having to close a socket

#67
I agree that automatic, deterministic resource management (as opposed to GC plus a dispose pattern of some kind) is highly desirable. But I think for the vast majority of projects, I would prefer automatic reference counting with the occasional weak reference to break cycles, particularly if that were the default for the language. I can appreciate that being explicit about resource ownership is an advantage in situations where one needs to absolutely minimize performance overhead. But for most applications, I think a system of ownership and borrowing like Rust's would just add extra cognitive load for the programmer for no appreciable gain. Of course, Rust isn't a language for most applications AFAIK; it's a systems language for performance-critical infrastructure like Servo. And Rust is probably not a language for blub programmers. But I think this blog post is absolutely right about the problems with GC plus a system for disposing of resources. It sucks that mainstream, blub-friendly language have mostly chosen GC over reference counting; I think automatic reference counting provides the right set of trade-offs for most applications.

Re: Rust means never having to close a socket

#68
post #65

"Rust achieves both of these features without runtime costs (garbage collection or reference counting), and without sacrificing safety." If it is freeing everything at the scope boundary it is incurring a huge runtime cost relative to garbage collected languages. GC scales with the number of live objects during GC while this scheme scales with the number of allocated objects. It is far more predictable than GC but I…

I'm a little bit confused. Every object that gets allocated has to be freed at some point. That cost is the same no matter how the memory is managed. Are you talking about an additional cost beyond "freeing memory that was allocated"?

It sounds like they are talking about a semi-space collector, which I can imagine might in theory be faster than scoped allocation since allocation is a simple pointer bump (just like stack allocation) but when garbage collecting you only have to traverse the live set. However, semi-space collectors can't collect non-memory resources without additional metadata, and they have a memory overhead of at least 2x the maximum live set. I have a hard time imagining even a semispace collector outperforming stack allocation.

Of course if your program is short-lived you can just allocate a sufficiently large heap, pointer bump the whole way and free all non-memory resources on program termination, which ought to be cheaper (but probably not that much in practice due to cache effects).

Re: Rust means never having to close a socket

#69
post #64
post #58

Earlier quoted context omitted.

Also, there are a whole host of very common types (integers, etc.) that actually want to be passed by value and copied. There's no point in making a pointer to an integer and then dereferencing the pointer in the callee when an integer fits into the same amount of space as a pointer in the first place. Very simple types (without heap pointers or destructors) can be passed by value and efficiently copied. You end up w…

If something is immutable (and doesn't own something else), it doesn't matter whether it's passed by value or reference. That's a decision the compiler can, and should, make, as an optimization decision. In Modula, where pass-as-read-only was the default, small items were passed by value and large ones by reference. "Small" is a CPU-dependent optimization and should be left to the compiler, not "user intuition". The…

There is an important distinction in Rust: ownership.

Copyable objects are (by definition) simple enough that copying them does not introduce aliasing. Objects with heap pointers or destructors cannot be copied, because now there would be two "owners" of those values.

For what it's worth, I've found that a lot of these assumptions ("it doesn't matter, let the compile optimize it") really remove the programmer from important power.

And before you find yourself saying "are you saying everyone should write everything in assembler?":

1. There is a distinction between writing code in assembler and having to use a `&` sigil for "by-reference" in a language that has lambdas, traits and many other high-level features.

2. You should generally use Rust for cases where performance characteristics are important, and where control over memory is important. If you're sure it's not important, by all means go with a higher-level language that abstracts it away.

I made this point (perhaps more clearly) in my talk at GoGaRuCo: http://www.youtube.com/watch?v=ySW6Yk_DerY

Re: Rust means never having to close a socket

#70
post #14

For files python[0] does not need an explicit with to close the file. It gets closed when references reach zero. [0] For the pedants repeating each other in the replies... Where by python, I mean the implementation that 98% of people use, called python, which the competition calls CPython. Yes it is a great feature of the implementation, like the deterministic memory management through reference counting.

That's an implementation detail of CPython. You rely on that behaviour, your code is broken:

* on pypy

* on jython

* on ironpython

That is, it's broken on Python, and it's not correct Python code. It's only correct CPython code.

Post reply on HN