Pre-RFC: Expressive Standard Library Paths

You have to for resilient macro_rules! macros as well, because item names are resolved at the call site. Example:

macro_rules! m {
    () => {Vec::<u8>::new()};
}

mod ok {
    fn f() {
        m!();
    }
}

mod nok {
    type Vec = u8;
    fn f() {
        m!(); // ~ Error: no function or associated item named `new` found for type `u8`
    }
}

This is not a great argument. Someone still has to write the macro, and that someone has to maintain the code the macro outputs. It's much easier to do so the less namespace noise that is present.


A "perfect"ly implemented macro_rules! macro that wanted to use the std's Vec would use $crate::_reexports::alloc::vec::Vec. A more prepared one night use $crate::_reexports::Vec, if they make their hidden _reexports module directly reëxport the types they are using.

A less paranoid macro would just use ::alloc::vec::Vec, or maybe ::std::vec::Vec. (If you're using std, there's no reason for your downstream to be #![no_std], and renaming not-std to be used at ::std is just asking for things to not work.)

The "proper" solution for the macro (decl or proc) is full def-site hygeine for all items, not just local bindings ("macros 2.0"). But reëxporting items at their shortest reasonable path would be an improvement for macros in the short term, where full hygeine is decidedly long-term.

1 Like

Adding a marker trait like Unpin to the prelude is specifically not a breaking change because it doesn't contain any new associated items like methods, which could conflict with the items of other traits the user has already brought into scope and cause a compile failure in existing code.

8 Likes

But that is easily solvable by IDE, at least by Intlij Rust plugin, just hit Alt+Enter and it generates use. I'm not sure if it works with Rust Analyzer.

Me personally is more pissed by warnings from unused imports, when you actively change code and remove some bits for sake of experimentation.

But I agree that unused ones should not go into final commit.

1 Like

I haven't used IntelliJ Rust in a little while, but other IntelliJ products have Ctrl+Alt+O (Optimize Imports) which can automatically remove unused imports.

Indeed. I haven't used hotkey, but manual way on unused imports with a tooltip. But my point was that when you experiment with code, you comment some lines out, then uncomment and import is useful again. It's a bit of annoyance.

If you're just experimenting, you can ignore the warning, and then fix the use lines before you commit.

1 Like

And if you have this situation a lot, and want to silence the warnings, you can also temporarily add #[allow(unused)] or #[allow(unused_imports)] to the module you're working on.

4 Likes

I'm a big fan of things like #![cfg_attr(test, allow(unused, dead_code))].

1 Like

As I said, I don't use an IDE, and I don't think Rust should require an IDE to be present for it to be ergonomic.

3 Likes

Indeed, so you could test that the following code actually typecheck and compiles, but you don't care to run a particular code.

I think I feel differently depending on how big and complicated the work to integrate into a editor it is. I agree you shouldn't need some nontrivial-startup-time IDE like Eclipse or VS. But where's that line? I generally don't consider Sublime Text to be "an IDE", but it can still give me buttons to apply structured suggestions from rust(c|ls|a|whatever). And that's a huge help that I would encourage everyone to setup. Note that even the playground has click-to-add-use.

Overall, though, I feel like if the problem is the uses, then changing use std::time::Instant; to use std::Instant; doesn't really move the needle to me. Similarly for the non-use version of std::time::Instant::now() vs std::Instant::now().

Everything in the root also feels like it's just asking for more use std::*;, too -- which C++ learned (from using namespace std; is not a good thing.

I think the answer here is prelude(s), not reexporting in the root of crates.

4 Likes

While I also think that use * is usually wrong, I don't think this particular argument is applicable in Rust. using namespace std; is problematic in C++ primarily because of C++ specific concerns, like dumping symbols into the global namespace (especially if done in header files) or adding a lot of overload resolutions. Start imports are much less problematic (and idiomatic) in Kotlin&Java.

To be clear, I don't have a position I want to argue for in this discussion, I just want to nitpick sightly flawed reasoning :slight_smile:

2 Likes

That's fair, using namespace std; is more like pub use std::*;, and the pub makes it much worse.

Pulling in arbitrary traits using * does do rust's form of "adding lots of overloads", though.

3 Likes

I'm not sure about Kotlin, but in Java star imports are considered bad, beside importing from standard runtime with widely known classes.

Motivation is to be able to distinguish imported classes/static method when viewing code without IDE, like in Github, for code-review.

1 Like

Don't forget to attach a warn(unused_must_use) (I wish there was a group lint for all the unused but must_use).

2 Likes

Why not propose that yourself, either here or to the appropriate tools team?

1 Like

But being able to write fn func(t: std::Instant) seems like an improvement. Most of the proposed additions are types.

2 Likes