Rust Lang Team
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Help
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- title: "Inherent trait impls RFC read design meeting" date: 2023-09-13 tags: T-lang, design-meeting, minutes url: https://hackmd.io/-UXw35J3RVCnccgWnlYxig --- - Feature Name: inherent-trait-impl - Start Date: 2018-03-27 # Summary [summary]: #summary Provides a mechanism to declare "inherent traits" for a type defined in the same crate. Methods on these traits are callable on instances of the specified type without needing to import the trait. # Motivation [motivation]: #motivation There are two similar cases where this is valuable: - Frequently used traits. Sometimes getting the right abstractions require breaking up a type's implementation into many traits, with only a few methods per trait. Every use of such a type results in a large number of imports to ensure the correct traits are in scope. If such a type is used frequently, then this burden quickly becomes a pain point for users of the API, especially if users do not care about writing generic code over traits. - Mapping object-oriented APIs. When mapping these APIs to rust, base classes are usually mapped to traits: methods on those base classes will need to be callable on any derived type. This is sub-optimal because to use a class method a user must now know which class in the hierachy defined that method, so that they can import and use the corresponding trait. This knowledge is not required when using the same API from an object-oriented language. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation The feature is implemented using a new attribute which can be applied to trait `impl` blocks: ```rust pub trait Bar { fn bar(&self); } pub trait ExtBar: Bar { fn ext_bar(&self); } impl<T: Bar> ExtBar for T { fn ext_bar(&self) { } } pub struct Foo; #[inherent] impl Bar for Foo { fn bar(&self) { println!("foo::bar"); } } // works thanks to specialization #[inherent] impl ExtBar for Foo { } impl Foo { fn foo(&self) { println!("foo::foo"); } } ``` The methods `bar` and `ext_bar` are now callable on any instance of `Foo`, regardless of whether the `Bar` and `ExtBar` traits are currently in scope, or even whether the `Bar` trait is publically visible. In other words if `Bar` and `ExtBar` defined in one crate and `Foo` and another, user of `Foo` will be able to explicitly depend only on the crate which defines `Foo` and still use the inherent traits methods. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation The `inherent` attribute in the above example makes the `impl` block equivalent to: ```rust impl Foo { #[inline] pub fn bar(&self) { <Self as Bar>::bar(self); } fn foo(&self) { println!("foo::foo"); } } ``` Any questions regarding coherence, visibility or syntax can be resolved by comparing against this expansion, although the feature need not be implemented as an expansion within the compiler. # Drawbacks [drawbacks]: #drawbacks - Increased complexity of the language. - Hides use of traits from users. # Rationale and alternatives [alternatives]: #alternatives - Define inherent traits either on a type `T` or on an `impl T { .. }` block. - Implement as part of the delegation RFC. - Do nothing: users may choose to workaround the issue by manually performing the expansion if required. The most viable alternative is delegation proposal, although arguably inherent traits and delegation solve different problems with the similar end result. The former allows to use trait methods without importing traits and the latter to delegate methods to the selected field. Nethertheless delegation RFC and this RFC can be composable with each other: ```Rust struct Foo1; struct Foo2; #[inherent] impl T1 for Foo1 { fn a() {} } #[inherent] impl T2 for Foo2 { fn b() {} } struct Bar { f1: Foo1, f2: Foo2, } impl Bar { // all methods from `T1` will be delegated as well // though `T1` will not be implemented for `Bar` delegate * to f1; } // method `b` will be accessable on `Bar` without importing `T2` #[inherent] impl T2 for Bar { delegate * to f2; } ``` # Prior art [prior-art]: #prior-art - https://github.com/rust-lang/rfcs/pull/2309 (previous closed PR) - https://github.com/rust-lang/rfcs/issues/1880 - https://github.com/rust-lang/rfcs/issues/1971 # Unresolved questions [unresolved]: #unresolved-questions - Do we want to introduce a new keword instead of using `#[inherent]`? In other words do we want to write `inherent impl A for B { .. }`? --- # Design meeting minutes Attendance: nikomatsakis, Josh, TC ## Why have we not done this already? nikomatsakis: I think this is a good idea. What were the concerns that blocked us from going forward in the past? TC: Some of the answer is in the rationale and alternatives, e.g., overlap with delegation.N Do we think there is any overlap? Josh: I'm not sure I see the overlap with delegation for the things we want inherent traits for. nikomatsakis: I think that delegation covers a lot more -- I always thought of it more like "delegating to a field", rather than "delegating to a trait". I guess at the end if `#[inherent]` becomes syntactic sugar for some kind of delegations, I can live with that. ## Are there gaps in how it solves problems? nikomatsakis: We currently can't introduce traits without boilerplate (duplicating methods). This seems to solve the problem. Does it? Am I missing something? joshtriplett: Breaking change here being requiring to import the trait? nikomatsakis: Yes, and ambiguity. joshtriplett: Are we confident it's not a breaking change to extract methods into a trait and make that trait impl inherent? nikomatsakis / TC: Seems like yes given the desugaring in the RFC. JT: Potential ambiguity, how does it resolve? ``` struct Type { ... } trait T1 { fn method(); } trait T2 { fn method(); } impl T1 for Type { ... } impl T2 for Type { ... } // In another crate: use firstcrate::{Type, T2}; let x: firstcrate::Type = get_a_type(); x.method(); ``` TC: It's equivalent to adding the method directly, so it would fall under what we call "trivial breakage". Josh: We could use a different desugaring that gives the inherent trait methods the priority of *traits* rater than *inherent methods*. Advantage: can't silently change behavior of existing code. Advantage: same resolution order as a trait method. Disadvantage: can't compatibly migrate from inherent method to inherent trait. TC: Keep in mind that the person writing the trait/impl may be different than the person using the trait and type and bringing them into scope. When moving an inherent method to a trait and using the `#[inherent]` attribute, the first person can know this will be compatible under the proposed desugaring. But otherwise that person cannot know this. NM: That's convincing. Josh: New warnings will show up for people who have imported the trait and now no longer need to. Are we OK with that? NM: Yeah, we want the warning. Josh: Agreed. ## Stdlib cases NM: Let's analyze some cases that come up in the standard library. Case 1a: ```rust struct Foo { } impl Foo { fn method(&self); } trait Bar { fn method(&self); } impl Bar for Foo { fn method(&self); } // want to add // trait Baz { } // #[inherent] // impl Baz for Foo { } ``` Here: `Foo::method` wins, we could move this to `#[inherent] impl Baz for Foo { .. }`, would retain semantics (and would still win). Case 2: ```rust struct Foo { } impl Foo { fn method(&self) { Bar::method(self) } } trait Bar { fn method(&self); } // add `#[inherent]` here impl Bar for Foo { fn method(&self); } ``` This is OK iff `Foo::method` already calls `Bar::method` Case 3: ```rust struct Foo { } trait Bar { fn method(&self); } // want to add `#[inherent]` to this impl Bar for Foo { fn method(&self); } ``` NM: I agree this is not a breaking change. If there was another trait with `method`, it'd already be ambiguous, so not a breaking change. JT: but they might not have imported `Bar`, so their use of the other trait is not ambiguous. NM: ah, ok. yes, you're right. ## The RFC has some things that seem like a distraction; should we get them removed before acceptance? Josh: Do we care about removing extraneous items from the RFC (e.g. reference to specialization)? Josh: Because this is an old RFC, let's just propose and commit those changes directly. *Consensus*: We'll propose and commit directly any editorial changes we have to the RFC. ## Let's propose FCP merge TC: We seem to have consensus this is a good idea. I propose that we propose FCP merge. Even if we want some editorial changes to the RFC, we could do that during that process. Josh: I'll do that. *Consensus*: We like this RFC with the current proposed desugaring and will propose FCP merge. (The meeting ended here.) --- # Things that came up after the meeting ## Handling of associated constants TC: The draft RFC doesn't mention associated constants at all. If we did not make these inherent when stabilizing inherent trait impls, it would not be backward compatible to do it later. We probably *do* want to make associated constants from the trait inherent on the type. Here's the proposal. We want to update the RFC to say that this: ```rust #[inherent] impl Trait for Foo { const CONST: u8 = 1; } ``` ...desugars to this: ```rust impl Foo { const CONST: u8 = <Self as Trait>::CONST; } ``` ## Handling of associated types TC: The draft RFC doesn't mention associated types at all. Since inherent associated types are not yet stable, we don't necessarily need to make a decision here, but we should think about it. When we *do* stabilize inherent associated types, we would need to commit to the behavior. Here's the proposal. Let's update the RFC to say that when the inherent associated types behavior in [RFC 195](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#inherent-associated-items) becomes stable, that this: ```rust #[inherent] impl Trait for Foo { type Assoc = Self; } ``` ...will desugar to this: ```rust impl Foo { type Assoc = <Self as Trait>::Assoc; } ``` ## Alternate syntax proposal TC: In the discussion above, we had left two major items unresolved. - How do we make blanket trait impls inherent? - How can we allow only *some* items from the trait impl to be made inherent? - This is especially tricky for associated functions and methods with a default implementation. (Part of the motivation for wanting to allow only some items to be made inherent is to prevent or to fix breakage caused when a trait later adds a new method with a default implementation whose name conflicts with the name of an existing inherent method.) Coming up with a syntax for these that combines well with the `#[inherent]` attribute could be challenging. One alternative that would make solving these problems straightforward is to add some syntax to the inherent `impl` block for the type. Given the desugaring in the RFC, there is some conceptual appeal here. (quaternic proposed this arrangement; TC is proposing the concrete syntax.) We can use `use` syntax to make this concise and intuitive. Here's an example: ```rust trait Trait1<Tag, T> { fn method0(&self) -> u8 { 0 } fn method1(&self) -> u8 { 1 } } trait Trait2<Tag, T> { fn method2(&self) -> u8 { 2 } fn method3(&self) -> u8 { 3 } fn method4(&self) -> u8 { 4 } } struct Tag; struct Foo<T>(T); impl<T> Foo<T> { // All methods and associated items of Trait1 become inherent, // except for `method0`. The inherent items are only visible // within this crate. pub(crate) use Trait1<Tag, T>::*; // Only `method2` and `method3` on Trait2 become inherent. pub use Trait2<Tag, T>::{method2, method3}; fn method0(&self) -> u64 { u64::MAX } } impl<T> Trait1<Tag, T> for Foo<T> {} impl<U: Trait1<Tag, T>, T> Trait2<Tag, T> for U {} ``` This solves another problem that we discussed above. How do we prevent breakage in downstream crates when a trait later adds a new method with a default implementation? Since a downstream crate might have made an impl of this trait for some local type inherent and might have an inherent method with a conflicting name, this could be breaking. We already handle this correctly for `use` declarations with wildcards. Any locally-defined items override an item that would otherwise be brought into scope with a wildcard import. We can reuse that same behavior and intuition here. When a wildcard is used to make all items in the trait inherent, any locally-defined inherent items in the `impl` prevent those items from the trait with the same name from being made inherent. Advantages: - It provides a syntax for adopting as inherent a blanket implementation of a trait for the type. - It provides a syntax for specifying which methods should become inherent, including methods with default implementations. - The wildcard import (`use Trait::*`) makes it very intuitive what exactly is happening and what exactly your API is promising. - The `use` syntax makes it natural for a locally-defined item to override an item from the wildcard import because that's exactly how other `use` declarations work. - `rust-analyzer` would probably support expanding a wildcard `use Trait::*` to an explicit `use Trait::{ .. }` just as it does for other `use` declarations, which would help people to avoid breakage. - We can support any visibility (e.g. `use`, `pub use`, `pub(crate) use`, etc.) for the items made inherent. Disadvantages: - There's some redundancy, especially when the items to make inherent are specifically named.

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully