Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement classes as per pcwalton's proposal #1726

Closed
catamorphism opened this issue Feb 1, 2012 · 24 comments
Closed

Implement classes as per pcwalton's proposal #1726

catamorphism opened this issue Feb 1, 2012 · 24 comments
Labels
A-frontend Area: Compiler frontend (errors, parsing and HIR) A-type-system Area: Type system C-enhancement Category: An issue proposing an enhancement or a PR with one.
Milestone

Comments

@catamorphism
Copy link
Contributor

I didn't see an issue for this, so I'm making one:

https://mail.mozilla.org/pipermail/rust-dev/2011-November/000929.html

@ghost ghost assigned catamorphism Feb 1, 2012
@nikomatsakis
Copy link
Contributor

classes should probably be able to declare interfaces that they implement, like impls

@lkuper
Copy link
Contributor

lkuper commented Feb 1, 2012

Now that the old object system is gone, this proposal would be not only an object system redesign but an object system resurrection. :) Not that there's anything wrong with that. But it seems to me that the original motivation for Patrick's proposal is gone now, since it had to do with the limitations of the old object system.

@graydon
Copy link
Contributor

graydon commented Feb 1, 2012

Grasping for metaphors, I think it more of an object-system dissection.

Believe me when I say nobody's more sympathetic to the concerns of going-around-in-circles than myself. I've been going around in these circles for years and years. But object systems were an area I had more technical-familiarity with the variants of, than opinions on the correct formulation of. I tend to dislike OO and habitually write very close-to-the-data-structure procedural and functional code. So I chose an object system that was too conservative and conflated too much.

This is ironic since I felt much of the virtue of the obj system you and I worked on was that it didn't conflate as much as classes tend to (say, in C++). Yet it still conflated too much:

  • Non-integration with type parameter system (no interface-bounding)
  • Mandatory boxed representation
  • Mandatory vtbl dispatch
  • Mandatory representation hiding
  • Only context for destructors

This conservatism was based on my own areas of interest and style, and an assumption that objects would be something we "didn't lean on much", and that most objects would be "big" and "long-lived" so the conservatism wouldn't hurt too much. This was a mistake. There are too many small-to-medium cases that benefit from object-like treatments, where one or more aspects of the conservatism would bite. People wound up avoiding the object system for its costs, and getting irate. We lost contributors and users over the apparent willingness to impose systemic costs; the obj system was one of those cost centers. Costs really matter. Take a look through http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf and reflect on the numbers. Common collections -- 80% overhead!

We've since split off all these conflated facets into orthogonal concepts, each of which can be used in its most-efficient form independent of the others.

  • A destructor -- and a destructor alone -- is a resource now, and can exist absent an object. No vtbl, no heap allocation. The compiler can inline the dtor into its point-of-destruction.
  • Representation hiding only happens when you access things via interfaces; the owner of a type may "look inside" the representation.
  • Vtbl dispatch only happens when passing an interface-bound type parameter or boxed interface value; impl dispatch can be and often is resolved statically.
  • Boxing only happens in heterogeneous boxed interface value cases, which are more rare than one might assume: lots of the time a vector of foo really does consist of 10,000 concrete foo values packed into a homogeneous array.
  • Making ifaces into concepts that can apply to any type meant we could start using them as interface bounds on type-parametric functions, which makes a huge expressiveness difference. It also means we'll get inlined, specialized variants of such functions "for free" as part of the work on monomorphizing.

So please try not to look at this as a "rip it out and put it right back in" exercise. It's nothing of the sort. The pieces being put-back-in have very different characteristics: they're orthogonal to one another, more universal, and tend to perform much better. The parts that remain to be supplied in the "new" system are:

  • A sugar form class for declaring a nominal record plus methods that have self.foo based access to the record fields. I'm not entirely convinced this matters, since it is pretty much pure sugar and we can do all the components in isolation already. Possibly including a subtype relation on extensions thereof. But I really don't like the sound of that, as it breaks representation uniqueness (what's the element size of an array of T if T has larger subtypes?) and makes type inference much uglier too.
  • A mechanism for additive combination / mixing-in of implementation suites, for some degree of code-reuse. This is likely to be accomplished through iface-parameterized traits.

I'm much more interested in trait than class, but we'll see which of these is most likely. Minimalism is worth striving for here: every new orthogonal concept, while potentially powerful, is cognitively costly.

Much of the material written in Patrick's email above is redundant or not applicable anymore, in the presence of the iface/impl work. Which is a good thing. We have ifaces that can apply to numbers and tuples and tags now. That's great.

@beoran
Copy link

beoran commented Feb 2, 2012

Coming from Ruby and passing by Go, I must say that I mostly agree with graydon. Still, some syntactic sugar for "class" (without subtyping) might be sweet, and might help people coming from other languages adjust.

@marijnh
Copy link
Contributor

marijnh commented Feb 6, 2012

@catamorphism I see you've started adding code for this. I don't think there is any up-to-date proposal related to classes. Please open a mailing list thread to discuss whathever it is you're implementing.

@catamorphism
Copy link
Contributor Author

@marijnh I'm implementing @pcwalton 's proposal from November (linked above), with no significant changes. I was gone while it was being discussed, so I was under the impression (from talking to Dave et al.) that the proposal had been accepted and it was just waiting for someone to have time to implement it. Is that not so?

@marijnh
Copy link
Contributor

marijnh commented Feb 6, 2012

In November, there were no ifaces or impls. I thought it was agreed during last week's meeting that someone (@nmatsakis? @pcwalton?) would send a new proposal to the mailing list.

@nikomatsakis
Copy link
Contributor

@marijnh I think sending a new proposal makes sense. However, I wouldn't anticipate many changes from what pcwalton originally proposed: modulo a few details, the ifaces and impls we have are a subset of the original proposal---though with the (granted, very significant!) added ability to have interface bounds. Do you think substantial changes are needed?

@marijnh
Copy link
Contributor

marijnh commented Feb 6, 2012

If people feel the original proposal still applies, that works for me. I had gotten another impression from last meeting, and still feel it'd be good to clearly define things like how a class declares which interfaces it implements, but I guess it'll fall out rather naturally.

@catamorphism
Copy link
Contributor Author

Maybe I was drifting off during that part of the meeting last week, as I don't remember it. In any case, I had thought that it would be pretty straightforward to just not implement the parts of the proposal that are already implemented :-) If anything comes up that requires a design discussion, I'll certainly post about it.

@catamorphism
Copy link
Contributor Author

Addendum to the proposal: As per in-person discussion on Tuesday, I think we have consensus to require self.x for references to a field x of class C within C, rather than bare x. I have not implemented this change yet.

@catamorphism
Copy link
Contributor Author

Current status: Classes with only fields work (either intra-crate or inter-crate). Methods don't work yet. I also haven't implemented the change to require self. These are the next steps on my to-do list. After that, it's on to traits.

@marijnh
Copy link
Contributor

marijnh commented Mar 17, 2012

If you aren't doing this already, please try to unify the code for class methods and impl methods as much as you can, so we don't end up with yet another different kind of callable thing.

@catamorphism
Copy link
Contributor Author

@marijnh Yes, I'm trying to do so.

@catamorphism
Copy link
Contributor Author

@nikomatsakis suggested allowing class methods to have additional ty params (other than the class's ty params), and I guess I don't see why not.

@marijnh
Copy link
Contributor

marijnh commented Mar 20, 2012

Impl/iface methods allow this, so it's needed to allow classes to implement arbitrary ifaces. There are some gotchas with properly instantiating the type parameters, but nothing problematic.

@graydon
Copy link
Contributor

graydon commented Mar 28, 2012

Pushing residual state of this bug to 0.3. The quantity of classes (first-cut, rough form) is working enough to satisfy 0.2.

@catamorphism
Copy link
Contributor Author

Question about traits (from Boris on IRC, paraphrased by me): It seems from reading the proposal that there is no way to declare an argument as "having trait X", since traits are not types. Instead, you would have to declare an iface, which introduces virtual calls. Is there a way to write code that's boundedly-polymorphic over things that have a certain trait, while ensuring that you won't introduce any virtual calls? And if there isn't, should the proposal be extended to support that? (In C++, this can be done when all subclasses are known; our ifaces aren't closed, so we can't necessarily make calls-through-ifaces non-virtual.)

@mictadlo
Copy link

Scala has traits http://www.scala-lang.org/node/126

@catamorphism
Copy link
Contributor Author

Pending issues (that I'm working on):

  • casting classes to ifaces
  • allocating classes outside the stack
  • "pub" sections
  • typestate for constructors
  • checking that self isn't used as a first-class value (except in a boxed class)
  • destructors
  • enforcing rules about copyability and sendability
  • privacy semantics (make private items class-private rather than instance-private)
  • require self as a prefix within the same class
  • traits
  • documentation

@nikomatsakis
Copy link
Contributor

Regarding using self as a first-class value: I think what we will eventually want is to assign self a region type. Then no special checks are needed.

Also, I was wondering if maybe we should "spin off" traits---not sure if anyone is free to work on them---but I think they are urgently desired. At least by me. :)

@catamorphism
Copy link
Contributor Author

If someone wanted to go ahead and work on traits, that would be fine. I guess it's not that likely that any of the issues with classes I listed would block traits, but some coordination might still be necessary.

@pcwalton
Copy link
Contributor

"Is there a way to write code that's boundedly-polymorphic over things that have a certain trait, while ensuring that you won't introduce any virtual calls?"

Bounded polymorphism doesn't introduce virtual calls in Rust, because of the way monomorphization works. (Assuming it works the way I think it does.)

@catamorphism
Copy link
Contributor Author

I'm going to consider this done and make issues for the remaining sub-problems.

@catamorphism catamorphism removed their assignment Jun 16, 2014
flip1995 pushed a commit to flip1995/rust that referenced this issue Nov 23, 2021
Improve `needless_borrow` lint

fixes: rust-lang#5327
fixes: rust-lang#1726
fixes: rust-lang#1212

This is merging `needless_borrow` into the `dereference` pass in preparation for `explicit_auto_deref`. `explicit_auto_deref` needs to implement most of what `needless_borrow` implements in order to work.

There is a minor regression here where `let x: &str = &x.deref()` will trigger `needless_borrow` without triggering `explicit_deref_methods`. Removing the redundant borrow will cause `explicit_deref_methods` to trigger. This will be fixed when `explicit_auto_deref` is implemented.

changelog: Lint `needless_borrow` when a borrow is auto-derefed more than once
changelog: Lint `needless_borrow` in the trailing expression of a block for a match arm
Kobzol pushed a commit to Kobzol/rust that referenced this issue Dec 30, 2024
bors pushed a commit to rust-lang-ci/rust that referenced this issue Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-frontend Area: Compiler frontend (errors, parsing and HIR) A-type-system Area: Type system C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests

8 participants