Skip to content

Commit 1986c3d

Browse files
committed
1 parent 24db227 commit 1986c3d

File tree

1 file changed

+57
-61
lines changed

1 file changed

+57
-61
lines changed

text/0000-packages-as-optional-namespaces.md

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,86 @@ For example, [unic](https://crates.io/search?page=1&per_page=10&q=unic-), [tokio
1717

1818
Regardless, it is nice to have a way to signify "these are all crates belonging to a single project, and you may trust them the same". When starting up [ICU4X](https://github.com/unicode-org/icu4x/), we came up against this problem: We wanted to be able to publish ICU4X as an extremely modular system of `icu-foo` or `icu4x-foo` crates, but it would be confusing to users if third-party crates could also exist there (or take names we wanted to use).
1919

20-
It's worth spending a bit of time talking about "projects" and "organizations", as nebulous as those terms are. This feature is *primarily* motivated by the needs of "projects". By this I mean a _single_ piece of software developed as multiple crates, for example `serde` and `serde/derive`, or `icu` and `icu/provider`, or `servo/script` and `servo/layout`. One would expect "projects" like this to live under a single Git repository according to the norms of project organization; they are logically a single project even if they are multiple crates.
20+
It's worth spending a bit of time talking about "projects" and "organizations", as nebulous as those terms are. This feature is *primarily* motivated by the needs of "projects". By this I mean a _single_ piece of software developed as multiple crates, for example `serde` and `serde::derive`, or `icu` and `icu::provider`, or `servo::script` and `servo::layout`. One would expect "projects" like this to live under a single Git repository according to the norms of project organization; they are logically a single project even if they are multiple crates.
2121

22-
The feature suggested here _might_ be used by "organizations" too, by which I mean a group of people who are coming together to build likely related crates, under the same "brand". One would expect "organizations" like this to use multiple repos under a GitHub organization, and the use case on crates.io would be to be able to have a similar "namespace" under which they can name their crates whatever they want, and prevent squatting. Personally I find this use case less compelling; and it is not the primary motivation behind the RFC. In particular, this use case is more prone to wanting to move crates between organizations and dependency management as a space is not in general super amenable to renames (though we can come up with ways to make this more pleasant).
22+
The feature suggested here _might_ be used by "organizations" too, by which I mean a group of people who are coming together to build likely related crates, under the same "brand". One would expect "organizations" like this to use multiple repos under a GitHub organization, and the use case on crates.io would be to be able to have a similar "namespace" under which they can name their crates whatever they want, and prevent squatting. Personally I find this use case less compelling; and it is not the primary motivation behind the RFC. In particular, this use case is more prone to wanting to move crates between organizations and dependency management as a space is not in general super amenable to renames (though we can come up with ways to make this more pleasant, considered out of scope for this RFC).
2323

2424

2525
The motivation here is distinct from the general problem of squatting -- with general squatting, someone else might come up with a cool crate name before you do. However, with `projectname-foo` crates, it's more of a case of third parties "muscling in" on a name you have already chosen and are using.
2626

2727
# Guide-level explanation
2828

29-
If you own a crate `foo`, you may create a crate namespaced under it as `foo/bar`. Only people who are owners of `foo` may _create_ a crate `foo/bar` (and all owners of `foo` are implicitly owners of `foo/bar`). After such a crate is created, additional per-crate publishers may be added who will be able to publish subsequent versions as usual.
29+
If you own a crate `foo`, you may create a crate namespaced under it as `foo::bar`. Only people who are owners of `foo` may _create_ a crate `foo::bar` (and all owners of `foo` are implicitly owners of `foo/bar`). After such a crate is created, additional per-crate publishers may be added who will be able to publish subsequent versions as usual.
3030

3131
The crate can be imported in Cargo.toml using its name as normal:
3232

3333
```toml
3434
[dependencies]
35-
"foo/bar" = "1.0"
35+
"foo::bar" = "1.0"
3636
```
3737

3838

39-
In Rust code, the slash gets converted to an underscore, the same way we do this for dashes.
39+
In Rust code, the colons still work:
4040

4141
```rs
42-
use foo_bar::Baz;
42+
use foo::bar::Baz;
4343
```
4444

45+
In case there is also an in-scope crate `foo` with an exported `bar` item, this will cause an ambiguity error unless both the item `foo::bar` and the crate `foo::bar` are actually resolving to the same item. This is similar to what rustc already does when it encounters in-use clashes in glob imports.
46+
4547
# Reference-level explanation
4648

47-
`/` is now considered a valid identifier inside a crate name Crates.io. For now, we will restrict crate names to having a single `/` in them, not at the beginning or end of the name, but this can be changed in the future.
49+
`::` is now considered valid inside crate names on Crates.io. For now, we will restrict crate names to having a single `::` in them, not at the beginning or end of the name, but this can be changed in the future.
4850

49-
When publishing a crate `foo/bar`, if the crate does not exist, the following must be true:
51+
When publishing a crate `foo::bar`, if the crate does not exist, the following must be true:
5052

5153
- `foo` must exist
5254
- The user publishing the crate must be an owner of `foo`
5355

54-
For the crate `foo/bar`, all owners of `foo` are always considered owners of `foo/bar`, however additional owners may be added. People removed from ownership of `foo` will also lose access to `foo/bar` unless they were explicitly added as owners to `foo/bar`.
56+
For the crate `foo::bar`, all owners of `foo` are always considered owners of `foo::bar`, however additional owners may be added. People removed from ownership of `foo` will also lose access to `foo::bar` unless they were explicitly added as owners to `foo::bar`.
5557

56-
Crates.io displays `foo/bar` crates with the name `foo/bar`, though it may stylistically make the `foo` part link to the `foo` crate.
58+
Crates.io displays `foo::bar` crates with the name `foo::bar`, though it may stylistically make the `foo` part link to the `foo` crate.
5759

58-
The [registry index trie](https://doc.rust-lang.org/nightly/cargo/reference/registries.html#index-format) may represent subpackages by placing `foo/bar` in `foo@/bar`, placed next to where `foo` is in the trie (i.e. the full path will be `fo/foo@/bar`).
60+
The [registry index trie](https://doc.rust-lang.org/nightly/cargo/reference/registries.html#index-format) may represent subpackages by placing `foo::bar` as just `foo::bar`.
5961

6062
No changes are made to `rustc`. When compiling a crate `foo/bar`, Cargo will automatically pass in `--crate-name foo_bar`, and when referring to it as a dependency Cargo will use `--extern foo_bar=....`. This is the same thing we currently do for `foo-bar`.
6163

62-
If you end up in a situation where you have both `foo/bar` and `foo-bar` as active dependencies of your crate, your code will not compile and you must [rename](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml) one of them.
64+
`rustc` will need some changes. When `--extern foo::bar=crate.rlib` is passed in, `rustc` will include this crate during resolution as if it were a module `bar` living under crate `foo`. If crate `foo` is _also_ in scope, this will not automatically trigger any errors unless `foo::bar` is referenced, `foo` has a module `bar`, and that module is not just a reexport of crate `foo::bar`.
65+
66+
The autogenerated `lib.name` key for such a crate will just be `bar`, the leaf crate name, and the expectation is that to use such crates one _must_ use `--extern foo::bar=bar.rlib` syntax. There may be some better things possible here, perhaps `foo_bar` can be used here.
6367

64-
The `features = ` key in Cargo.toml continues parsing `foo/bar` as "the feature `bar` on dependency `foo`", however it now will unambiguously parse strings ending with a slash (`foo/` and `foo/bar/`) as referring to a dependency, as opposed to feature on a dependency. Cargo may potentially automatically handle the ambiguity or error about it.
6568

6669
# Drawbacks
6770

68-
## Slashes
71+
72+
## Namespace root taken
73+
Not all existing projects can transition to using namespaces here. For example, the `unicode` crate is reserved, so `unicode-rs` cannot use it as a namespace despite owning most of the `unicode-foo` crates. In other cases, the "namespace root" `foo` may be owned by a different set of people than the `foo-bar` crates, and folks may need to negotiate (`async-std` has this problem, it manages `async-foo` crates but the root `async` crate is taken by someone else). Nobody is forced to switch to using namespaces, of course, so the damage here is limited, but it would be _nice_ for everyone to be able to transition.
74+
75+
76+
## Slow migration
77+
78+
Existing projects wishing to use this may need to manually migrate. For example, `unic-langid` may become `unic::langid`, with the `unic` project maintaining `unic-langid` as a reexport crate with the same version number. Getting people to migrate might be a bit of work, and furthermore maintaining a reexport crate during the (potentially long) transition period will also be some work. Of course, there is no obligation to maintain a transition crate, but users will stop getting updates if you don't.
79+
80+
A possible path forward is to enable people to register aliases, i.e. `unic-langid` is an alias for `unic::langid`.
81+
82+
83+
## Requires rustc changes
84+
85+
There are alternate solutions below that don't require the _language_ getting more complex and can be done purely at the Cargo level. Unfortunately they have other drawbacks.
86+
87+
88+
# Rationale and alternatives
89+
90+
This change solves the ownership problem in a way that can be slowly transitioned to for most projects.
91+
92+
## Slash as a separator
93+
94+
**For discussions about separator choice, please discuss them in [this issue](https://github.com/Manishearth/namespacing-rfc/issues/1) to avoid overwhelming the main RFC thread.**
95+
96+
A previous version of the RFC had `/` as a separator. It would translate it to `foo_bar` in source, and disambiguated in feature syntax with `foo/bar/` vs `foo/bar`. It had the following drawbacks:
97+
98+
99+
### Slashes
69100
So far slashes as a "separator" have not existed in Rust. There may be dissonance with having another non-identifier character allowed on crates.io but not in Rust code. Dashes are already confusing for new users. Some of this can be remediated with appropriate diagnostics on when `/` is encountered at the head of a path.
70101

71102

@@ -81,12 +112,7 @@ Furthermore, slashes are ambiguous in feature specifiers (though a solution has
81112
default = ["foo/std"]
82113
```
83114

84-
85-
## Namespace root taken
86-
Not all existing projects can transition to using namespaces here. For example, the `unicode` crate is reserved, so `unicode-rs` cannot use it as a namespace despite owning most of the `unicode-foo` crates. In other cases, the "namespace root" `foo` may be owned by a different set of people than the `foo-bar` crates, and folks may need to negotiate (`async-std` has this problem, it manages `async-foo` crates but the root `async` crate is taken by someone else). Nobody is forced to switch to using namespaces, of course, so the damage here is limited, but it would be _nice_ for everyone to be able to transition.
87-
88-
89-
## Dash typosquatting
115+
### Dash typosquatting
90116

91117
This proposal does not prevent anyone from taking `foo-bar` after you publish `foo/bar`. Given that the Rust crate import syntax for `foo/bar` is `foo_bar`, same as `foo-bar`, it's totally possible for a user to accidentally type `foo-bar` in `Cargo.toml` instead of `foo/bar`, and pull in the wrong, squatted, crate.
92118

@@ -95,33 +121,21 @@ We currently prevent `foo-bar` and `foo_bar` from existing at the same time. We
95121
One thing that could mitigate `foo/bar` mapping to the potentially ambiguous `foo_bar` is using something like `foo::crate::bar` or `~foo::bar` or `foo::/bar` in the import syntax.
96122

97123

98-
## Slow migration
99-
100-
Existing projects wishing to use this may need to manually migrate. For example, `unic-langid` may become `unic/langid`, with the `unic` project maintaining `unic-langid` as a reexport crate with the same version number. Getting people to migrate might be a bit of work, and furthermore maintaining a reexport crate during the (potentially long) transition period will also be some work. Of course, there is no obligation to maintain a transition crate, but users will stop getting updates if you don't.
101-
102-
A possible path forward is to enable people to register aliases, i.e. `unic-langid` is an alias for `unic/langid`.
103-
104-
# Rationale and alternatives
105-
106-
This change solves the ownership problem in a way that can be slowly transitioned to for most projects.
107124

108-
## Using identical syntax in Cargo.toml and Rust source
125+
### Using identical syntax in Cargo.toml and Rust source
109126

110-
This RFC in its current form does not propose changes to the Rust compiler to allow slash syntax (or whatever) to parse as a Rust path. Such changes could be made (though not with slash syntax due to parsing ambiguity, see [below](#Separator choice) for more options); this RFC is attempting to be minimal in its effects on rustc.
127+
The `/` proposal does not require changes to Rust compiler to allow slash syntax (or whatever) to parse as a Rust path. Such changes could be made (though not with slash syntax due to parsing ambiguity, see [below](#Separator choice) for more options); this RFC is attempting to be minimal in its effects on rustc.
111128

112129
However, the divergence between Cargo.toml and rustc syntax does indeed have a complexity cost, and may be confusing to some users. Furthermore, it increases the chances of [Dash typosquatting](#Dash typosquatting) being effective.
113130

114-
## `foo::bar` on crates.io and in Rust
115-
116-
117-
**For discussions about separator choice, please discuss them in [this issue](https://github.com/Manishearth/namespacing-rfc/issues/1) to avoid overwhelming the main RFC thread.**
131+
Some potential mappings for `foo/bar` could be:
118132

119-
While I cover a bunch of different separator choices below, I want to call out `foo::bar` in particular. If we went with `foo::bar`, we could have the same crate name in the Rust source and Cargo manifest. This would be _amazing_.
120-
121-
Except, of course, crate `foo::bar` is ambiguous with module `bar` in crate `foo` (which might actually be a reexport of `foo::bar` in some cases).
122-
123-
This can still be made to work, e.g. we could use `foo::crate::bar` to disambiguate, and encourage namespace-using crates to ensure that `mod bar` in crate `foo` either doesn't exist or is a reexport of crate `foo::bar`. I definitely want to see this discussed a bit more.
133+
- `foo::bar`
134+
- `foo::crate::bar`
135+
- `foo::/bar`
136+
- `~foo::bar`
124137

138+
and the like.
125139

126140
## Whole crate name vs leaf crate name in Rust source
127141

@@ -137,7 +151,7 @@ A major drawback to this approach is that while it addresses the "the namespace
137151

138152
**For discussions about separator choice, please discuss them in [this issue](https://github.com/Manishearth/namespacing-rfc/issues/1) to avoid overwhelming the main RFC thread.**
139153

140-
A different separator might make more sense.
154+
A different separator might make more sense. See the [previous section](#slash-as-a-separator) for more on the original proposal of `/` as a separator.
141155

142156
We could continue to use `/` but also use `@`, i.e. have crates named `@foo/bar`. This is roughly what npm does and it seems to work. The `@` would not show up in source code, but would adequately disambiguate crates and features in Cargo.toml and in URLs.
143157

@@ -157,27 +171,13 @@ Note that unquoted dots have semantic meaning in TOML, and allowing for unquoted
157171
We could reverse the order and use `@`, i.e. `foo/bar` becomes `bar@foo`. This might be a tad confusing, and it's unclear how best to surface this in the source.
158172

159173

160-
## Separator mapping
161-
162-
The proposal suggests mapping `foo/bar` to `foo_bar`, but as mentioned in the typosquatting section, this has problems. There may be other mappings that work out better:
163-
164-
- `foo::bar` (see section above)
165-
- `foo::crate::bar`
166-
- `foo::/bar`
167-
- `~foo::bar`
168-
169-
and the like.
170-
171-
172174
## User / org namespaces
173175

174176
Another way to handle namespacing is to rely on usernames and GitHub orgs as namespace roots. This ties `crates.io` strongly to Github -- currently while GitHub is the only login method, there is nothing preventing others from being added.
175177

176178
Furthermore, usernames are not immutable, and that can lead to a whole host of issues.
177179

178-
## Registry trie format
179-
180-
Instead of placing `foo/bar` in `foo@/bar`, it can be placed in `foo@bar` or something else.
180+
The primary goal of this RFC is for _project_ ownership, not _org_ ownership, so it doesn't map cleanly anyway.
181181

182182
# Prior art
183183

@@ -187,12 +187,8 @@ Namespacing has been discussed in https://internals.rust-lang.org/t/namespacing-
187187

188188
# Unresolved questions
189189

190-
- Is `/` really the separator we wish to use?
191-
- How do we avoid ambiguity in feature syntax
192-
- Is there a way to avoid `foo/bar` turning in to the potentially ambiguous `foo_bar`?
193-
- Can we mitigate some of typosquatting?
194190
- How can we represent namespaced crates in the registry trie?
195-
- How do we represent namespaced crates in the URLs of crates.io and docs.rs?
191+
- How exactly should the Cargo.toml `lib.name` key work in this world, and how does that integrate with `--extern` and `-L` and sysroots?
196192

197193
# Future possibilities
198194

0 commit comments

Comments
 (0)