Live data from Hacker News

Abstraction without overhead: traits in Rust

blog.rust-lang.org

101–110 of 150 posts

Re: Abstraction without overhead: traits in Rust

#101
post #84

Earlier quoted context omitted.

I didn't claim (and neither did kibwen, who unlike me is a major Rust contributor) that Rust's approaches around memory management didn't change. But your claim that "Lifetimes aren't regions" is false -- lifetimes are squarely in the 30-year research history of region systems.

A region calculus[1], such as Tofte described, is not what I see in Rust as it exists today. It could've been so with typestate, if I understood the intent behind typestate correctly. If what Rust has is understood to be regions, then I need a couple of words for distinguishing the two. Ordinarily, I refer to what Rust/C++ have as "ownership types" and what exists in research as regions/RBMM/RC. [1]: http://www.resea…

As someone not terribly familiar with the literature but familiar with Rust's type system, I just spend a few minutes googling various pages about region based memory management (including Tofte + Talpin) as well as skimming your link (not very hard, I admit), and I don't really understand what it lets you do (that doesn't exist in a roughly analogous form in Rust). Well, there's the fact that most of the papers describe region inference, while Rust is fully explicit, but that doesn't seem critical to the scheme to me. Out of curiosity, I'd be interested to see you elaborate on the difference.

Also, I do not know what other 'PLs with similar facilities' exist with anywhere near as much effort put into them (other than Cyclone, which is dead). You mentioned C++, but it doesn't have lifetime checking at all, which is of course a core feature of Rust...

Re: Abstraction without overhead: traits in Rust

#102
post #5

Great article. The main bit of new information for me was that while Rust supports dynamic dispatch, its implementation has a noticeable difference from C++. In C++, the vtable pointer is in the object itself. In Rust, it's stored inside what is essentially a "fat pointer." Pointers to traits ("trait objects") are actually two pointers: the pointer to the vtable and the pointer to the actual object. This seems to hav…

The other major downside being the extra level of indirection, which could potentially cause an extra cache miss per method call. A drop in the park bucket compared to most other languages, but a serious consideration for many of the areas that C and C++ dominate.

Re: Abstraction without overhead: traits in Rust

#103
post #35

Earlier quoted context omitted.

Stop it, they're unions. :) Among the target market, people are going to intimately familiar with the use of "enum" and "union" from the C language. Rust's concept of a single object that can store exactly one of several types of sub-objects matches one quite closely, and not the other. Having a runtime tag and affirmative checking doesn't change the nature of the thing. We don't call "cars" something different when…

I'll have to admit enums are where I gave up the first time I read the Rust tutorial. They seem almost completely unrelated to enums in C and other languages.

> They seem almost completely unrelated to enums in C and other languages.

They're not. Payload-less enums devolve to C enums. Rust's enum simply build the enum+union enumeration pattern into the language, and allow leaving out the "union" part.

Re: Abstraction without overhead: traits in Rust

#104
post #101

Earlier quoted context omitted.

A region calculus[1], such as Tofte described, is not what I see in Rust as it exists today. It could've been so with typestate, if I understood the intent behind typestate correctly. If what Rust has is understood to be regions, then I need a couple of words for distinguishing the two. Ordinarily, I refer to what Rust/C++ have as "ownership types" and what exists in research as regions/RBMM/RC. [1]: http://www.resea…

As someone not terribly familiar with the literature but familiar with Rust's type system, I just spend a few minutes googling various pages about region based memory management (including Tofte + Talpin) as well as skimming your link (not very hard, I admit), and I don't really understand what it lets you do (that doesn't exist in a roughly analogous form in Rust). Well, there's the fact that most of the papers desc…

C++ doesn't cover the full extent of safety Rust offers at all, but there is a fair bit of overlap in how the functionality is packaged up WRT ownership types.

I checked the docs and it looks like at least http://doc.rust-lang.org/0.12.0/guide-lifetimes.html#named-l... is covered which was one of my objections. I still don't like that I can't design my own linearly typed constraints, but it appears that was never a goal to begin with. Not surprising given the lack of emphasis on expressive types.

As it stands my only options for linear'ish types are indexed Monads in Haskell or building a model of linear types in a proof assistant or DTPL.

Re: Abstraction without overhead: traits in Rust

#105
post #100
post #42

Earlier quoted context omitted.

I don't think that's true. Dictionary-passing (or something equivalent) attaches the table of methods to the functions which take typeclass instances as arguments (with extra, hidden arguments), while Rust's Box attaches the table of methods to the object pointer itself. So you can have a list of Box in Rust, but you can't have a list of Show in Haskell. You have to set up the extra indirection yourself using somethi…

> but you can't have a list of Show in Haskell I'm not sure exactly what you mean, but my naive interpretation is that you can: Prelude> :t map show map show :: Show a => [a] -> [String]

I'm not exactly sure what they mean, since the following works, and is pretty much a drop-in replacement for a Box trait object in Rust.

  data Showable = forall a. Show a => Showable a
This allows for

  [Showable 1, Showable "foo", Showable 'x']
(It requires the ExistentialQuantification language feature.)

Re: Abstraction without overhead: traits in Rust

#106
post #100
post #42

Earlier quoted context omitted.

I don't think that's true. Dictionary-passing (or something equivalent) attaches the table of methods to the functions which take typeclass instances as arguments (with extra, hidden arguments), while Rust's Box attaches the table of methods to the object pointer itself. So you can have a list of Box in Rust, but you can't have a list of Show in Haskell. You have to set up the extra indirection yourself using somethi…

> but you can't have a list of Show in Haskell I'm not sure exactly what you mean, but my naive interpretation is that you can: Prelude> :t map show map show :: Show a => [a] -> [String]

That takes a list of a, where a is some type which is Show. You can't have a grab-bag of different Show things in there.

Re: Abstraction without overhead: traits in Rust

#107
post #100
post #42

Earlier quoted context omitted.

I don't think that's true. Dictionary-passing (or something equivalent) attaches the table of methods to the functions which take typeclass instances as arguments (with extra, hidden arguments), while Rust's Box attaches the table of methods to the object pointer itself. So you can have a list of Box in Rust, but you can't have a list of Show in Haskell. You have to set up the extra indirection yourself using somethi…

> but you can't have a list of Show in Haskell I'm not sure exactly what you mean, but my naive interpretation is that you can: Prelude> :t map show map show :: Show a => [a] -> [String]

That's not a list of Show, that's a list of a single type that instantiates Show.

The difference being that in your code you can only put in a single type at a time, eg [Int] or [String], but not both Int and String under the common Show interface type.

Re: Abstraction without overhead: traits in Rust

#108
post #5

Great article. The main bit of new information for me was that while Rust supports dynamic dispatch, its implementation has a noticeable difference from C++. In C++, the vtable pointer is in the object itself. In Rust, it's stored inside what is essentially a "fat pointer." Pointers to traits ("trait objects") are actually two pointers: the pointer to the vtable and the pointer to the actual object. This seems to hav…

The other major downside being the extra level of indirection, which could potentially cause an extra cache miss per method call. A drop in the park bucket compared to most other languages, but a serious consideration for many of the areas that C and C++ dominate.

Extra indirection? There is less indirection.

With the C++ approach the data is one pointer away but the vtable is two pointers away.

With the Rust approach both data and vtable are just one pointer away.

Re: Abstraction without overhead: traits in Rust

#109

Earlier quoted context omitted.

I apologise in advance that this may not be most appropriate place to ask this question. I am looking for a Rust tutorial. It looks like there was one, but it was deprecated in favour of 'the book'. But 'the book' doesn't seem to have a tutorial yet. Are there any tutorial's running through how to build some small piece of working software. I found the Golang tutorial, where you build a very basic blog extremely enjo…

The book basically has two sets of tutorials: the "Syntax and Semantics" section is a bottom-up tutorial, and the "Learn Rust" section is a project-based, more top-down one. It's true that only one chapter of Learn Rust has landed at the moment. It's basically what I'm doing right now. Should have two or three more chapters over the next few days.

Great, thanks Steve.

Re: Abstraction without overhead: traits in Rust

#110

Earlier quoted context omitted.

The other major downside being the extra level of indirection, which could potentially cause an extra cache miss per method call. A drop in the park bucket compared to most other languages, but a serious consideration for many of the areas that C and C++ dominate.

Extra indirection? There is less indirection. With the C++ approach the data is one pointer away but the vtable is two pointers away. With the Rust approach both data and vtable are just one pointer away.

That's assuming that a "full pointer" (i.e. an unpredictable address to an arbitrary point in heap memory) is the only way to get a reference to an object. What about if you want to stick a bunch of objects in a vector, and iterate over them linearly? In the C++ approach, the data will have a nice linear cache access pattern. In the Rust approach (unless I'm missing something) you'd be storing a vector of (double sized) pointers, to god knows where in heap memory... suffering a cache miss on every access, on average.
Post reply on HN