rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.9k stars 1.56k forks source link

Nested function scoped type parameters #3562

Closed jake-87 closed 5 months ago

jake-87 commented 8 months ago

Allow type parameters to be used in nested functions. Removes E0401.

This error was first introduced (at the latest) in 2015. After almost ten years of development and changes in Rust, I believe it's time to reconsider it. TLDR: Allow the below.

fn foo<T>(x: T) -> T {
    fn bar(y: T) -> T {
        y
    }
    bar(x)
}

Rendered

jake-87 commented 8 months ago

Apologies, I do not know how to assign tags. I believe this proposal should at least have T-lang.

Jules-Bertholet commented 8 months ago

Would this also allow using type parameters from an outer function inside the inner function's body, even when they do not appear in the inner function's signature? E.g.,

fn outer<T: Default>() {
    fn inner() {
       let _ = T::default();
    }
}

This would mean that the compiler has to inspect inner's body before being able to tell that it's generic. Also, one of the main uses of nested inner functions is for deliberately avoiding separate monomorphizations; this change could make it easier to accidentally introduce genericity when it's not desired.

Also, how does this interact with attributes that require a non-generic function, like #[export_name] and #[no_mangle]?

Finally, should const or static items also be able to use generic parameters from the outer function?

jake-87 commented 8 months ago

No, I am not proposing to allow that example, for the exact reasons you've raised.

I'm unsure what you mean by "interacting with #[export_name] and #[no_mangle]" - if there's no generic parameters, you can't use this feature.

With regards to static and const, They already cannot, and I see no reason to change that. In those two examples, it's quite reasonable that they cannot use a generic parameter.

SkiFire13 commented 7 months ago

I'm unsure what you mean by "interacting with #[export_name] and #[no_mangle]" - if there's no generic parameters, you can't use this feature.

I believe they meant something like this:

fn foo<T>(x: T) -> T {
    // This attribute requires `bar` to not be generic, but this then requires determining that doesn't use `T`
    #[no_mangle]
    fn bar(y: i32) -> i32 {
        y
    }
    // This cannot be allowed, but notice how it has the same signature as `bar`!
    #[no_mangle]
    fn baz(y: i32) -> i32 {
        T::some_function(y)
    }
    bar(x)
}

With regards to static and const, They already cannot, and I see no reason to change that. In those two examples, it's quite reasonable that they cannot use a generic parameter.

IMO it would be confusing if functions can use generic parameters declared outside and static/const can't. Also, what about structs, traits and impls?

jake-87 commented 7 months ago

I would propose to reject both of your examples, noting the same reasoning that @Jules-Bertholet provided. Whether a function can be #[no_mangle] (or similar) should be determinable from the type signature, which it will still be - simply an occurs check.

Correct me if i'm wrong, but static and const should not be able to as there is no way to obtain a static/const value without arbitrary code execution, rending this useless anyway due to the generic nature of the type. Perhaps the error message could be changed from E0401 to something more informative.

Structs being able to utilize this seems reasonable (although perhaps not productive? may encourage longer functions using more internal structs - happy to take further comment on this one)

However, traits and impls would probably not benefit from this, and further complicating trait resolution seems a fool's errand. I do not want to "half-arse" this feature, but these two seem much more work then they're worth (I've also never seen a local trait)

teor2345 commented 7 months ago

I would propose to reject both of your examples, noting the same reasoning that @Jules-Bertholet provided.

It might be helpful to add the examples and rationale to the RFC. It could go in the rationale and alternatives section, but I'm not sure if that's the best place.

jake-87 commented 7 months ago

I will, yes - I've ran into some busyness, but I'll do it when I find the time.