Skip to content
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

Allow use in more scopes #3708

Open
Anatoly03 opened this issue Oct 4, 2024 · 1 comment
Open

Allow use in more scopes #3708

Anatoly03 opened this issue Oct 4, 2024 · 1 comment

Comments

@Anatoly03
Copy link

Right now, the keyword use in modules and block expressions is reserved for including modules, crates, external sources and the use of it is fairly simply. An unsuccessful proposal to expand this syntax to other scope was suggested in #1976 seven years ago. Let's first review some drawbacks with the current approach. To make this post readable, I will use the word "import" to indicate the act of attaching a crate, module or external binary with use.

Adding use to traits

Right now, importing affects the entire module, so the only solution to restrict them to a code block, is to add a new module. This could get out of hand really quickly, as every code block may need a different subset of imports, leading to badly nested code. It's may also be not clear where an import is used if it is defined at the root scope.
In the scenario where you wouldn't want a developer to use particular imports within a scope, you couldn't do much about it, and the developer wouldn't know it either. For example, let's say that you work on a parser project, and have different kind of parsing-related traits. To indicate, that an import is only valid for a trait, you currently cannot do so within the implementation scope. However adding imports at the root scope may negatively affect other traits in a large project.

impl TraitA for Struct {
   use import;
   pub user import::func;
// ^^^ public `use` imports are not supported in `trait`s or `impl`s
}

impl TraitB {
    // You cannot use the `import` in this scope, and a developer who is not familiar with the code base will know that.
}

The drawback is, that it's not clear if the imports would be part of a trait implementation. To avoid this problem, assuming that imports are private, the best approach would be not to allow public imports. For trait implementations, if an import uses the symbol that is ambiguous with a method, the import needs to be renamed to another identifier with as.

On a more clear example, when working with different parsings crates like nom, proc-macro2 and implementation scopes that rely on buit-in methods purely, I expect to have control over in which scope an import is active in. This way using imports outside of scope will be noticed by the compiler.

The next benefit comes with procedural macros. Let's say, you have a macro extension that takes a struct or an enum, and spits out the same struct with additional implementation code. If we were to add second structure #[hello] Hello2 to the code below, we would get a problem of duplicate imports, and a pretty unreadable one, too! cannot determine resolution for the import. The ability to import per scope would fix this in particular.

#[hello]
pub struct Hello {}
// something is here (?)
pub struct Hello {}
use something;
impl Hello {
    pub fn hi() {
        something::call();
    }
}

Adding use .. in

lambda-fairy and kennytm pointed out how absurd a use statement would look like in match, struct and enum expressions. I believe the latter two should be atomic like they are now. The solution would be to use OCaml-inspired in syntax where the import prepends the expression. The following code example shows this fix applied towards this message.

let foo = use std::cmp::Ordering::* in
    Foo {
        a: Greater,
        b: Less,
    }

The alternative proposed by Kimundi is to use the keyword where followed by imports instead. I find both variants to be acceptable.

Conditional Imports

Currently, there is no good way to to conditionally include multiple imports, other than marking every single import with particular features. The above will already reduce most of the clutter caused by unused imports with a combination of feature flags, so this is another benefit.

@kennytm
Copy link
Member

kennytm commented Oct 5, 2024

use … in

note that if an additional indentation + lexical scope is acceptable you could already do

    let foo = {
        use std::cmp::Ordering::*;
        Foo {
            a: Greater,
            b: Less,
        }
    };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants