We should definitely bring scope into crates

Hey everyone!

I noticed that some crates are related and maintained by the same author. They use a naming convention to show this relationship, like tokio, tokio-utils, tokio-stream, or the same for boa (boa_ast, boa_gc, boa_cli). This pattern is pretty common in big and popular crates.

I think it would be great if cargo supported scopes, like npm does. For boa, it could be @boa, @boa/cli, @boa/gc, and so on. This would have a big benefit: all crates provided by the same peer would be categorized under a certain scope. This way, users can be sure that a random crate belongs to a specific provider.

For example, if we create a boa_random crate, without the scope, it’s hard to tell if it’s maintained by the boa team or not. But @boa/random clearly shows that it’s a boa team crate.

I think we should definitely bring scope into our crates.

PS: Cargo’s features can’t replace scopes. features is not suitable for completely different crates.

1 Like

You might be interested in 3243-packages-as-optional-namespaces - The Rust RFC Book

2 Likes

These are typically called "namespaces," not "scopes," and this idea has already been discussed quite a lot.

2 Likes

I feel like namespaces need to come, as soon as possible. it is essential for the rust ecosystem to stay healthy as it grows faster and faster. and IMO it is necessary for a central registry with contribution from individuals.

for instance, docker images from registries are namespaced, npm packages are namespaced (scoped), vscode extensions are namespaces on the marketplace. and notably, github repositores are namespaced, can you imagine your github repositories must have unique names across entire github?

also, some systems mitigate the name collision issue using convensions, such as go and java, I would consider them practically namespaced. however, the situation for languages like go and java is a bit different, they import packages by (hierachical) paths, e.g. import com.example.helloworld.cli.Main; in java, which typically correspond to (arbitrarily deep) filesystem paths; as opposed to import by (namespace qualified) names, e.g. require('@example-com/helloworld-cli') in js, which usually use flat or very shallow filesystem storage.

there's also some discussion about package name squatting recently.

the problem with cargo packages is, how to migrate to the namespaced system in a backward compatible way, and how should we import a namespaced package in code?

for the first problem, maybe we can use docker.io for reference, e.g. if you do docker pull ubuntu:latest, it resolves to a "official" library namespace, namely, docker.io/library/ubuntu:latest, and the web url for the image uses an underscore as the namespace, i.e. hub.docker.com/_/ubuntu.

similarly we can reserve a "global" (or "default', or "_", to be bikeshedded) namespace, where all old packages should go, but new versoins are not allowed to publish there, the maintainers must setup a redirect target, and newer version of the same package must be published under their registered namespace. when cargo updates a legacy package, it can use the redirect to pull the new version, and prints a warning message to the user to inform the package renaming.

for the second problem, I have absolutely no idea. I'm not into the compiler at all, so I can just make wild hypothesis.

currently, we can only import a flat crate, i.e.

extern crate foo;

I'd imagine we have to do something like:

// warning:
// these are my imaginary syntax, they are NOT real rust
extern crate foo::{bar, baz};
extern namespace foo {
    crate bar;
    crate baz;
}
extern mod foo::{bar, baz}
extern crate foo {
    mod bar;
    mod baz;
}
// other posibilities...

again, I don't know what is feasible or what is preferred, I'm just imagining.

1 Like

I prefer namespaces too, but scope is the term used by npm.

1 Like

The compiler implementation could be easy by using only the local name in rust code and leaving the namespace specification to cargo, e.g.:

[dependencies]
log = { version="*", from="rust-lang" }

For the unlikely case that one uses two equally named crates but from different namespaces, one of them needs to be renamed in cargo.toml with the "package" key.

There are problems arising from TOML and how cargo uses it though:

  • TOML does not allow two keys with the same name.
  • The nice shortcut of using "namespace/package" as literal key is not possible, since literals must not contain a slash.

I think it would be better not to interpret the key as the name used by the registry but as the name to be used in rust code. If the two names should differ, than the key should specify the rust code name and the registry name should be specified in the value.

2 Likes

I don't love the idea of requiring every dependency specification to be a full dict instead of a string; it loses some of the beginner-friendly elegance of choosing TOML in the first place.

But I'm not sure how one would change that. Perhaps something like foo = "bar/1.2.3", but it seems odd to put the namespace and version together. Maybe the best solution is just to use quoted keys?

1 Like

I use the term ‘scope’ to describe a new idea that could coexist with the existing crate structure. From the perspective of cargo, scope refers to the specific team’s products (like tokio), while namespace is used to refer to those crates within the code.

I think we’ve hit a bit of a snag, but I think we can figure it out. We’ve been stuck in a loop, and I think we need to break free. Let’s come up with a plan and move forward.