-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking issue for fundamental
feature
#29635
Comments
This comment has been minimized.
This comment has been minimized.
See rust-lang/futures-rs#479 for an example of something this would solve. |
Triage: not aware of any change. |
So here's a fun bug due to lacking validation:
#![feature(fundamental)]
#[fundamental]
pub struct Foo;
extern crate a;
impl Clone for a::Foo {
fn clone(&self) -> Self { a::Foo }
} I think the rule we should enforce is that |
I think it would make more sense to say that outside the defining crate, the coherence rules for |
How do people feel about just stabilizing Even if a more precise way is found, it's not the end of the world to have Has anyone actually encountered a situation where more control was needed? |
This needs some evaluation of what use cases will still exist after the current work on negative reasoning becomes available and stable. It's possible there's still a use case for it after that, in which case we need to look at that more closely, and figure out if it can be supported in a reasonable way. Or it's possible that what's left should be perma-unstable, given the coordination difficulties of doing this outside the standard library. |
I have a library in https://github.com/LightningCreations/lccc (the actual implementation/host side, rather than the stdlib/target side, so I don't have privileged unstable access), xlang_abi, which contains types that are necessary for communicating between modules soundly, and includes reimplementations of some standard library types (including Box). I would like to be able to support fundamental types on, at the very least, |
I have a use case for |
FWIW, here is how |
The challenge with the approach that |
How about instead of marking the entire type as So:
#![feature(fundamental)]
pub struct Foo<#[fundamental] S, T> {
// PhantomData ceremony
}
struct Bar;
// This is legal, because S is Bar and we own Bar
impl Clone for a::Foo<Bar, ()> {
fn clone(&self) -> Self {
a::Foo {
// PhantomData ceremony
}
}
}
// This is illegal, because Bar is in T. S is () and we don't own ()
impl Clone for a::Foo<(), Bar> {
fn clone(&self) -> Self {
a::Foo {
// PhantomData ceremony
}
}
} Alternative syntax: #[fundamental(S)]
pub struct Foo<S, T> {
// PhantomData ceremony
} |
I think that the subset of Doing so would also improve the teachability of the feature as a whole: the question _what does
Stabilizing a subset of it with an explicit notation regarding both the generic parameter, and the trait involved, would thus not only improve the teachability / understandability of the feature and of its semantics, it would also improve the diagnostics when misused, as well as reduce the possitibility for footguns. Instead of
|
To be honest, I'd rather that `impl<#[downstream] T: ?Sized>` * for
Box<T>;` be spelt `#[downstream_may_impl] pub struct Box<#[downstream] T:
?Sized, A: Allocator>`.
…On Tue, 19 Apr 2022 at 09:44, Daniel Henry-Mantilla < ***@***.***> wrote:
I think that the subset of #[fundamental]-for(-generic)-types (≠
#[fundamental]-for-traits) which could be stabilized would have to not
only be explicit around not only around the generic parameters like you've
suggested, @idanarye <https://github.com/idanarye>, but also around the
specific traits that downstream users may implement.
Doing so would also improve the teachability of the feature as a whole:
the question _what does #[fundamental] stand for is quite complex to
answer:
- it's either for traits, or for generic types. For a non-generic type
it doesn't make sense for instance.
- In the case of generic types, it yields a universal promise,
future-wise, regarding a lack of blanket impls over that generic parameter, *for
any trait in existence*. It's not only subtle, but a huge burden
maintenance-wise. For instance, breaking that promise would not be easy to
spot from a library author point of view (I guess the rationale is that the
stdlib has enough pairs of eyes over it so that this footgun doesn't
matter).
Stabilizing a subset of it with an explicit notation regarding both the
generic parameter, *and the trait involved*, would thus not only improve
the teachability / understandability of the feature and of its semantics,
it would also improve the diagnostics when misused, as well as reduce the
possitibility for footguns.
Instead of #[fundamental], have a #[downstream_may_impl] annotation
- Or just #[may_impl], or keep #[fundamental]. That detail is left for
later bikeshedding.
#[downstream_may_impl]
impl<#[downstream] S, T> Serialize for Foo<S, T>;
this would thus not overlap with:
impl<T> Serialize for Foo<String, T> { … }
since String can't be a #[downstream] type, but it would overlap with
impl<S : Clone, T> Serialize for Foo<S, T> { … }
Since <S : Clone> and <#[downstream] S> may overlap.
------------------------------
This yields intuitive expectations w.r.t. the classic coherence rules, and
is explicit over both the #[downstream] parameter,
fundamental/coherence-wise, as well as the specific traits involved.
With it, using pseudo-code, one coukd even finally *illustrate* the
semantics of Box being fundamental, for instance:
#[downstream_may_impl]
impl<#[downstream] T : ?Sized> * for Box<T>;
where we'd be able to see there is yet another magical thing with a
#[fundamental] generic type: a * quantification over all possible traits.
—
Reply to this email directly, view it on GitHub
<#29635 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABGLD27YBWXG2KCF6FSMPBLVF22ELANCNFSM4BTTUBWA>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
What do you mean "as well"? With your suggestion a type can be fundamental over different generic parameters as long as it's for different traits, but it should still not be fundamental over different generic paramters for the same trait. If It's up for for the individual type to decide which generic parameters to be fundamental over, and in Unless... did you mean you avoided doing something like that? #[bikeshed::this_crate_shall_not_impl]
impl<#[downstream] T : ?Sized, A: Allocator> * for Box<T, A>; If so - why not? Even if you allow this, it'd still be up for the downstream crate to include the allocator in their own |
Heh, precisely because I didn't want to think about 2-ary-fundamental generic types I skipped the alloc parameter 😅 , but @idanarye it's nonetheless an interesting question; I actually find that my syntax can help explain what possible semantics Given: //! `::upstream`
pub trait Trait {}
#[bikeshed::this_crate_shall_not_impl]
impl<#[downstream] T : ?Sized, #[downstream] A: Allocator> Trait for Box<T, A>; and: //! `::a`
struct Local;
impl<A> ::upstream::Trait for ::upstream::Box<crate::Local, A> {} then it would error stating that this blanket-over-the-second-param impl overlaps with the upstream's: impl<#[downstream] T : ?Sized, #[downstream] A: Allocator> Trait for Box<T, A>; precisely because a downstream crate could write //! `::b`
struct Local;
impl ::upstream::Trait for ::upstream::Box<::a::Local, crate::Local> {} That is, a @chorman0773 the |
That's the thing - this declaration should be rejected by the compiler: impl<#[downstream] T : ?Sized, #[downstream] A: Allocator> Trait for Box<T, A>; The rule should be that only one generic parameter is allowed to be impl<#[downstream] T : ?Sized, A: Allocator> Trait for Box<T, A>; Or this: impl<T : ?Sized, #[downstream] A: Allocator> Trait for Box<T, A>; Of course, with this syntax that gives downstream permissions for implementing specific traits, one could do: impl<#[downstream] T : ?Sized, A: Allocator> Trait1 for Box<T, A>;
impl<T : ?Sized, #[downstream] A: Allocator> Trait2 for Box<T, A>; And then we could have: //! `::a`
struct Local;
impl<A> ::upstream::Trait1 for ::upstream::Box<crate::Local, A> {} //! `::b`
struct Local;
impl ::upstream::Trait2 for ::upstream::Box<::a::Local, crate::Local> {} |
I think improving the current behaviour of this might be a blocker for #32838, since it's not at all obvious that we want to let people implement things for |
@scottmcm Only if the entire type is made fundamental. If the fundamentalness is limited to one generic parameters, as per my suggestion, then one could implement traits for |
Add `Box<[T; N]>: TryFrom<Vec<T>>` We have `[T; N]: TryFrom<Vec<T>>` (rust-lang#76310) and `Box<[T; N]>: TryFrom<Box<[T]>>`, but not this combination. `vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case. **Insta-stable, so needs an FCP** (I tried to make this work with `, A`, but that's disallowed because of `#[fundamental]` rust-lang#29635 (comment))
We have a use case for A
You can also pass the We want to allow users to implement inherent methods on Would it make sense to extend fundamental types to cover inherent impls as well? See also this Zulip thread. |
There's no disambiguation mechanism for conflicting things on inherent impls, so that would mean it's a breaking change to add anything, which seems too strong. I thing the expectation will remain that such methods be added via extension traits, not inherent methods, even on |
Isn't it already a breaking change to add methods? Consider this: mod foo {
pub struct Foo;
}
mod bar {
pub trait Bar {
fn bar(&self);
}
impl Bar for super::Foo {
fn bar(&self) {
println!("Bar.bar");
}
}
}
use foo::Foo;
use bar::Bar;
fn main() {
Foo.bar();
} If we add this to impl Foo {
pub fn bar(&self) {
println!("Inherent bar");
}
} It'll change the behavior of |
In the "Library Evolutions" RFC, this was noted as being an acceptable
breaking change for the rust standard library. The Rust Semver RFC I
believe also calls it acceptable in a minor release.
…On Thu, Jan 26, 2023 at 14:47 Idan Arye ***@***.***> wrote:
Isn't it already a breaking change to add methods?
Consider this:
mod foo {
pub struct Foo;}
mod bar {
pub trait Bar {
fn bar(&self);
}
impl Bar for super::Foo {
fn bar(&self) {
println!("Bar.bar");
}
}}
use foo::Foo;use bar::Bar;
fn main() {
Foo.bar();}
If we add this to mod foo:
impl Foo {
pub fn bar(&self) {
println!("Inherent bar");
}}
It'll change the behavior of Foo.bar().
—
Reply to this email directly, view it on GitHub
<#29635 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABGLD27YXQWMFYEFGIHKZPTWULIDVANCNFSM4BTTUBWA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Sop wouldn't doing the same for |
@idanarye No. It's allowed for traits because you have to option of using UFCS to write it in a way that can't conflict no matter what other methods people add to other traits. |
@scottmcm wrote:
This is an acceptable tradeoff for us. We won't have inherent |
Add `Box<[T; N]>: TryFrom<Vec<T>>` We have `[T; N]: TryFrom<Vec<T>>` (#76310) and `Box<[T; N]>: TryFrom<Box<[T]>>`, but not this combination. `vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case. **Insta-stable, so needs an FCP** (I tried to make this work with `, A`, but that's disallowed because of `#[fundamental]` rust-lang/rust#29635 (comment))
Add `Box<[T; N]>: TryFrom<Vec<T>>` We have `[T; N]: TryFrom<Vec<T>>` (#76310) and `Box<[T; N]>: TryFrom<Box<[T]>>`, but not this combination. `vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case. **Insta-stable, so needs an FCP** (I tried to make this work with `, A`, but that's disallowed because of `#[fundamental]` rust-lang/rust#29635 (comment))
Add `Box<[T; N]>: TryFrom<Vec<T>>` We have `[T; N]: TryFrom<Vec<T>>` (#76310) and `Box<[T; N]>: TryFrom<Box<[T]>>`, but not this combination. `vec.into_boxed_slice().try_into()` isn't quite a replacement for this, as that'll reallocate unnecessarily in the error case. **Insta-stable, so needs an FCP** (I tried to make this work with `, A`, but that's disallowed because of `#[fundamental]` rust-lang/rust#29635 (comment))
This feature flag, part of RFC 1023, is not intended to be stabilized as-is. But this issue tracks discussion about whether some external feature it needed. Perhaps there is a cleaner way to address the balance between negative reasoning and API evolution. See the RFC for details.
The text was updated successfully, but these errors were encountered: