rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.2k stars 12.44k forks source link

Tracking Issue for non-lifetime binders #108185

Open compiler-errors opened 1 year ago

compiler-errors commented 1 year ago

This is a tracking issue for "non-lifetime binders" (https://github.com/rust-lang/types-team/issues/81). The feature gate for the issue is #![feature(non_lifetime_binders)].

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.

Steps

Unresolved Questions

None yet

Implementation history

compiler-errors commented 1 year ago

Having recently thoroughly reviewed the guidelines for experimenting with new feature gates (https://github.com/rust-lang/rust/pull/109010#issuecomment-1477235351 😅), it dawned on me that that the T-types MCP that I did in rust-lang/types-team#81 may have been insufficient to justify landing experimental support for non-lifetime binders in the compiler...

Would anyone from @rust-lang/lang like to chime in here? Not exactly sure if it warrants a T-lang second + kicking off an FCP if we're trying to follow procedure, or if it's not worth it a month after landing the PR adding support. In any case, I'm trying to at least retroactively do my due dilligence here (and will do so for feature gates I add in the future in the future, sorry about that again lol).

Given the MCP from a T-types perspective, it's already confirmed that we on T-types would like to have non-lifetime binders support in the language for experimentation (especially given the pretty gnarly modifications to the trait solver that will be needed to get non-lifetime binders working fully), but if T-lang is not confident about this experimental feature (or would like an RFC), I guess I could un-land it as well. Let me know!

nikomatsakis commented 1 year ago

I'm happy to serve as a lang-team second here. I don't think we need to do the full MCP, but if anyone on @rust-lang/lang has concerns about the idea of supporting things like for<T>, please raise them now.

Just for good measure, I'll nominate for lang team triage meeting.

@rustbot labels +I-lang-nominated

scottmcm commented 1 year ago

To me, accepting GATs was a de-facto "yes, you can experiment with for<T>", since the parallels are so strong.

nikomatsakis commented 1 year ago

@rustbot labels -I-lang-nominated

rebenkoy commented 7 months ago

hello, I think this feature causes ICE in current nightly. issue: https://github.com/rust-lang/rust/issues/119941

compiler-errors commented 7 months ago

@rebenkoy: Thanks, but no need to comment on this tracking issue -- opening a bug report is sufficient. The feature is marked as incomplete because non-lifetime binders still has bugs. That issue will be fixed in due time.

warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes
 --> src/lib.rs:1:12
  |
1 | #![feature(non_lifetime_binders)]
  |            ^^^^^^^^^^^^^^^^^^^^
zvolin commented 7 months ago

I'm curious if that could work with const generics. I'm currently working with Multihash and MultihashDigest and it's often a pain to propagate the parameter all the time. Consider this example:

trait ToArray<const N: usize> {
    fn to_array(&self) -> [u8; N];
}

struct Array<const N: usize>([u8; N]);

impl<const N: usize> ToArray<N> for Array<N> {
    fn to_array(&self) -> [u8; N] {
        self.0
    }
}

struct Vec2(Vec<u8>);

impl Vec2 {
    fn from_arr<const N: usize, A>(other: A) -> Self
    where
        A: ToArray<N>,
    {
        Self(other.to_array().to_vec())
    }
}

fn main() {
    let other = Array([0; 20]);
    Vec2::from_arr::<20, _>(other);
}

Specifying this 20 doesn't read nice. I'd love to be able to write it like so

impl Vec2 {
    fn from_arr<A>(other: A) -> Self
    where
        A: for<const N: usize> ToArray<N>,
    {
        Self(other.to_array().to_vec())
    }
}

fn main() {
    let other = Array([0; 20]);
    Vec2::from_arr(other);
}
c3potheds commented 7 months ago
impl Vec2 {
    fn from_arr<A>(other: A) -> Self
    where
        A: for<const N: usize> ToArray<N>,
    {
        Self(other.to_array().to_vec())
    }
}

fn main() {
    let other = Array([0; 20]);
    Vec2::from_arr(other);
}

I don't think that would do what you think. The bounds you put on A say "A can be turned into an array of any size", not "A can be turned into an array of some size".

You'd want to use:

impl Vec2 {
    fn from_arr<A, const N: usize>(other: A) -> Self
    where
        A: ToArray<N>,
    {
        Self(other.to_array().to_vec())
    }
}

I could imagine a FromArray<const N: usize> trait where a universal type would be useful, though:

trait FromArray<T, const N: usize> {
    fn from_array(a: [T; N]) -> Self:
}

// Implement the trait for one specific size.
impl<T> FromArray<T, 2> for (T, T) {
    fn from_array(a: [T; 2]) -> Self {
        (a[0], a[1])
    }
}

// Implement the trait for all sizes.
impl<T, const N: usize> FromArray<T, N> for Vec<T> {
    fn from_array(a: [T; N]) -> Self {
        a.into_iter().collect()
    }
}

// Require something that implements the trait for all sizes.
fn create_test_data<A>() -> [A; 3]
where
    for <const N: usize> A: FromArray<i32, N>,
{
    [
        A::from_array([0]),
        A::from_array([0, 1]),
        A::from_array([0, 1, 2]),
    ]
}