Weird idea: use as crate

Specifically, allow use some::multipart::path as crate foo; with semantics equivalent to extern crate some_multipart_path as foo; if that path were a crate, even if it isn't. I.e. it behaves as a normal use, but additionally (if defined in the root module namespace[1]) it makes the name also always available as ::foo.

Why? Proc macros often need access to runtime libraries, and for attributes/derives, typically the best they can do is assume the relevant crates are available as ::cratename. use as crate would enable you to use derives/attributes exported from a wrapper crate without either the wrapper crate needing to create a copy of the macros using itself as the entry point or supplying a crate path to every invocation.

E.g. bevy uses and derives encase::ShaderType for GPU "sharable" types. encase_derive of course expects ::encase to work, but bevy doesn't want their users to need to have a dependency on (a potentially different version of) encase, so bevy_encase_derive exposes a copy of the same derive code (shared via encase_derive_impl) which uses their pub use at bevy::render::render_resource::encase instead[2]. If we had this feature, bevy could have users (potentially via macro) do use bevy::render::render_resource::encase as crate encase; instead, and the normal derive would work. (Though they would probably still use the wrapped macro, to bypass the need for the use, unless glob using a module with a pub use as crate would make ::name available. But that seems almost certainly a bit too much.)

Yes, this can be abused to obfuscate and make ::name not refer to a package, but I don't think the potential abuse avenues are any worse than the existing package renaming functionalities.

It could additionally permit just use some::path as crate; inferring the name to use from the last path segment, but I don't think that's necessary and the explicitness is helpful here, since we're turning a multi segment name into a single segment name. Also, I chose to spell this as just as crate name since there's nothing particularly extern about using paths as crates, but it could also be spelled as extern crate name.

For consistency, this would likely be permitted in nested use trees, but idiomatically always formatted into its own use statement.

  1. That edition2018+ (path reform) extern crate only mounts to the extern prelude when written in the root module of the crate is at best surprising, and does make extern crate almost equivalent to just a use when not at the crate root. I opened an issue to warn for this, because it's nonobvious behavior and can always be written clearer as a regular use.

    It might make sense to restrict use as crate to the crate root even if extern crate isn't, but I instead chose straight copying the semantics here. ↩︎

  2. Actually, bevy sniffs for your manifest, and will use any of bevy::render::…, bevy_render::…, or encase based on which it thinks is most likely to work. ↩︎


What happens when two different libraries push the same name to the root namespace of the binary target? Libraries' root namespace is intentionally "chroot"ed (I can't put it in another way) to avoid name conflicts with other libraries. It's the same reason why we have orphan rules.

1 Like

Different packages have different namespaces and still can't influence each other. That's why you can't use ::depname and refer to a dependency you don't declare locally. Each package still has exclusive control over its own accessible namespace. (Semi-exception: if you expand a macro at root scope, it could contain extern crate as / use as crate and put new names in the ::package namespace.) If you put the same name twice, you get an error, exactly the same as saying extern crate dep; or use ::dep; twice.

There's nothing intended to be special about binaries here, they're just another crate like any other.

(So the bevy example isn't a great one.)

macro-dependencies is IMO a better solution to the proc-macro issue (e.g. it handles not-bevy = { package = "bevy" } which this would still have issues with). Is there any other usecase for it?

IIRC it doesn’t have to be root scope, any extern crate anywhere gets hoisted to ::.


Try it. It doesn't. This surprised me too.

The big one is hacking pseudo peer dependencies, but it's also generally useful as an alias, being able to act roughly similar to a crate local prelude, actually.

1 Like