Skip to content

Tracking issue for auto traits #13231

Open

Description

@flaper87

View all comments

This is a tracking issue for RFC 19 / RFC 127.
The feature gate for the issue is #![feature(auto_traits)].

Note

This feature used to be called opt-in built-in traits (gated behind optin_builtin_traits) and
used to include negative negative impls (now gated behind negative_impls; tracking issue: #68318).

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Implementation history

Completed Checklist (Historical)

Checklist

Here is a check-list of code to write and tricky scenarios to be sure we handle:

Unresolved Questions

Activity

  1. self-assigned this
    on Mar 31, 2014
  2. changed the title [-]opt-in built-in trait bounds RFC tracker[/-] [+]opt-in built-in bounds traits RFC tracker[/+] on Mar 31, 2014
  3. pnkfelix commented on Apr 3, 2014

    @pnkfelix
    Contributor

    Marking 1.0, P-backcompat-lang.

  4. added this to the 1.0 milestone on Apr 3, 2014
  5. nikomatsakis commented on Apr 15, 2014

    @nikomatsakis
    Contributor

    cc me

  6. flaper87 commented on Apr 22, 2014

    @flaper87
    ContributorAuthor

    I'm actively working on this. Unfortunately, I've moved quite slowly because of other bugs I had to fix. I hope to be able to submit a PR this week.

  7. 257 remaining items

  8. GoldsteinE commented on Sep 27, 2023

    @GoldsteinE
    Contributor

    Oops, I mixed up “opting out” and “opting in” in my original message. Either way, the point stands: I don’t see a reason to opt out of arbitrary auto traits unless you’re doing something that can affect this particular system. Author of the auto trait guarantees that it’s safe to impl it if all the fields impl it.

  9. steffahn commented on Dec 11, 2023

    @steffahn
    Member

    For Ungil, I’ve found the issue

    which is that an auto-trait is unable to restrict access to certain types even if those types always feature a non-'static lifetime as provided by to a higher-kinded for<'a> FnOnce(SomeType<'a>)-style callback.

    I’m relatively certain that the AutoreleaseSafe example suffers from the same issue.

    Of course one approach would be to declare scoped-tls unsound on the (arguably quite thin) grounds that the standard library only sets precedent that 'static data is allowed to be shared via thread-local storage. Assuming we don’t declare scoped-tls unsound, an auto traits feature seems entirely unable to be a solution for sound API in either of the API cases for AutoreleaseSafe nor Ungil.

  10. GoldsteinE commented on Dec 11, 2023

    @GoldsteinE
    Contributor

    Oh, that’s really sad. If scoped-tls is valid (and I don’t see a reason for it not to be), then auto trait Ungil can only make pyo3 less-obviously-unsound instead of sound. IMHO that’s still a valid reason for it to be an auto trait, but it’s really unfortunate that it doesn’t just make it clean and sound.

  11. madsmtm commented on Dec 11, 2023

    @madsmtm
    Member

    Hmm, I don't think that's a problem for objc2's AutoreleaseSafe, since the only type it's used on is AutoreleasePool<'pool>, whose only purpose is to carry a lifetime (it's a ZST that doesn't actually carry any data), which it seems like scoped-tls-hkt manages to properly bind to the execution of the closure.

    So even though we can access the pool, we can't actually do anything bad with it, since it's only the lifetime that we care about, and that is still correctly bounded. See the following example for details.

    ```cargo
    [dependencies]
    # requires `auto_traits` feature
    objc2 = { version = "0.5", features = ["unstable-autoreleasesafe"] }
    scoped-tls-hkt = "0.1"
    ```
    
    use objc2::rc::{autoreleasepool, AutoreleasePool, Id};
    use objc2::runtime::NSObject;
    use scoped_tls_hkt::scoped_thread_local;
    
    fn main() {
        autoreleasepool(|pool1| {
            scoped_thread_local!(static POOL: for<'pool> AutoreleasePool<'pool>);
    
            POOL.set(pool1, || {
                let obj = autoreleasepool(|_pool2| {
                    POOL.with(|pool1| {
                        Id::autorelease(NSObject::new(), pool1)
                    })
                });
    
                // If we manage to get here, then that's unsound, since the object
                // would have been released in the second autorelease pool.
    
                println!("{obj:?}");
            });
        });
    }

    If you manage to produce a similar example that does compile, then I'd love to know!

  12. steffahn commented on Dec 11, 2023

    @steffahn
    Member

    @madsmtm Here we go:

    use std::cell::Cell;
    
    use objc2::rc::{autoreleasepool, AutoreleasePool, Id};
    use objc2::runtime::NSObject;
    use scoped_tls_hkt::scoped_thread_local;
    
    struct PoolHolder<'p> {
        pool1: AutoreleasePool<'p>,
        obj_cell: &'p Cell<Option<&'p NSObject>>,
    }
    trait IPoolHolder {
        fn autorelease(&self, id: Id<NSObject>);
    }
    impl<'pool> IPoolHolder for PoolHolder<'pool> {
        fn autorelease(&self, id: Id<NSObject>) {
            self.obj_cell.set(Some(Id::autorelease(id, self.pool1)));
        }
    }
    
    fn main() {
        autoreleasepool(|pool1| {
            scoped_thread_local!(static POOL_HOLDER: for<'p> &'p dyn IPoolHolder);
    
            let obj_cell = &Cell::new(None);
    
            POOL_HOLDER.set(&PoolHolder { pool1, obj_cell }, || {
                autoreleasepool(|_pool2| {
                    POOL_HOLDER.with(|pool1_holder| pool1_holder.autorelease(NSObject::new()))
                });
    
                let obj: &NSObject = obj_cell.get().unwrap();
    
                // If we manage to get here, then that's unsound, since the object
                // would have been released in the second autorelease pool.
    
                println!("{obj:?}");
            });
        });
    }
  13. added a commit that references this issue on Aug 8, 2024
  14. Zenithsiz commented on May 6, 2025

    @Zenithsiz

    Is it possible to make an auto trait not depend on the fields of a type?

    I'd like to be able to have an auto trait auto trait Everything {}, and a type struct Wrapper<T>(T) such that an auto-trait is always implemented for Wrapper regardless of T: Everything (unless there's an explicit impl !Everything for Wrapper<...> {}).

    Further motivation

    I have a normal trait with a default implementation depending on an auto trait:

    pub auto trait WantsAutoSet {}
    
    pub trait Set {
    	fn set(&self);
    }
    
    // "Default" impl for all types.
    impl<S: WantsAutoSet> Set for S {
    	fn set(&self) {}
    }

    I then have 2 types, Normal which just wants the default impl and Special which wants to do a special implementation:

    // Wants default `Set` impl
    pub struct Normal<T>(T);
    
    
    // Wants special `Set` impl
    pub struct Special<T>(T);
    
    impl<T> !WantsAutoSet for Special<T> {}
    
    impl<T> Set for Special<T> {
    	fn set(&self) {}
    }

    The issues happen downstream, where a consumer of Normal cannot call set on a generic T, only on T: WantsAutoSet:

    // Works
    fn set_normal_with_explicit_auto<T: WantsAutoSet>(normal: Normal<T>){
        normal.set();
    }
    
    // Does not work
    fn set_normal<T>(normal: Normal<T>) {
        normal.set();
    }

    (See the playground)

    This can be fixed by just using impl<T> WantsAutoSet for Normal<T> {}, (or by having Normal not have a type parameter), but I have a lot of these Normal-like types and I don't want to need to make this impl for each one, since at that point I'd just make it a normal trait instead of an auto trait.

    Also these Normal-like types do need ownership of T for the Set impl, I can't just use PhantomData to wave it away somehow.

    (Also note that this is somewhat like specialization, but I can't just use actual specialization because the Special impl of Set has different bounds that are incompatible (i.e. not a subset), and I'd also like to "disable" the default implementation because it'd inefficient for Special, which wouldn't work with specialization.)

  15. changed the title [-]Tracking Issue for auto traits (auto_traits) -- formerly called opt-in built-in traits (optin_builtin_traits)[/-] [+]Tracking issue for auto traits[/+] on Apr 12, 2026
  16. lygstate commented on May 8, 2026

    @lygstate
    Contributor

    When this can be stablized, #149335 also want this and blocks #154046

    I really want io to be mved from std into core.

  17. madsmtm commented on May 28, 2026

    @madsmtm
    Member

    See #13231 (comment), it likely won't be stabilized outside of the standard library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-auto-traitsArea: auto traits (e.g., `auto trait Send {}`)A-trait-systemArea: Trait systemA-type-systemArea: Type systemB-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCF-auto_traits`#![feature(auto_traits)]`P-mediumMedium priorityS-tracking-perma-unstableStatus: The feature will stay unstable indefinitely.T-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions