RFC: Convention: do not prefix exports with the module's name#356
Conversation
|
You could also imagine yet another alternative: special case only fn open(path: &Path) -> Result<File>
fn open(path: &Path) -> IoResult<File>In the first version, it's clear that It is likely the (Still, I personally prefer the proposed version of the rule, with no special cases.) |
|
@aturon I've been thinking about this, and while I agree it is a good goal, certain parts of Rust push users in the opposite direction. The basic pattern I've seen in the std libraries is to prefix the type name, and then provide a naked impl for just about all functionality, then users can write: use std::io::net::tcp::TcpSocket;
use std::io::net::udp::UdpSocket;
TcpSocket::bind(...);
UdpSocket::bind(...);and not worry about importing the With these guidelines use std::io::net::tcp::Socket;
use std::io::net::udp::Socket;won't work, so it would natural to write: use std::io::net::tcp;
use std::io::net::udp;
udp::Socket::bind(...);
tcp::Socket::bind(...);If Socket were the only public/"important" type in each of those modules however, it almost makes sense to go full ML, and write: use std::io::net::tcp_socket;
use std::io::net::udp_socket;
udp_socket::bind(...); // returns udp_socket::T;
tcp_socket::bind(...); // returns tcp_socket::T;The point of all this, is naked impls introduce multiple ways to structure code with the exact same semantics. On on extreme there is the All these have downsides, and even if one is chosen, it seems likely that code following the other patterns will emerge in practice. |
|
I'm not entirely following your comment. In particular, you say:
but as the guidelines mention, you're free to import types with a prefix to resolve ambiguity: use std::io::net::tcp::Socket as TcpSocket;
use std::io::net::udp::Socket as UdpSocket;Essentially, this pushes resolving overlaps between names to the client of an API (where the overlaps are actually created) rather than the provider of an API (who would otherwise have to guess at which overlaps will occur). |
|
As an aside, it's almost possible, using associated items, for modules with a single "important" type to just be that type, which would actually be quite nice. Unfortunately, without HKT, this isn't sufficiently expressive to handle common cases (e.g. iterator types). |
|
I completely agree with the proposed design; no special casing please! Good show 👏 |
|
As we can alias the imports, I don't see the need to duplicate information. So +1 to this, with no special cases. |
|
@aturon I'm sorry. I guess my comment just boils down to that it would be nice if there was only one possible way of doing things, and no need to choose one of client- or provider-side ambiguity in many places. The ML What I should have said first is: A. given the current state of Rust, I think your conventions are the best ones to have. B. I am curious how you interpret your conventions regarding names like |
|
It's great that you are tackling this @aturon. How does this work with traits? Referring to stuff prefixed by the module name is useful, but you need to import traits directly in order to access their methods. |
|
@bjz, I think aliasing can help here. |
|
I largely agree with the principle here, but a notable drawback of the completely purist approach (where This is an issue, for example, in generated documentation. An extreme illustration of the problem is provided by Haskell's numeric-prelude package, which is widely reviled for its author's preferred convention of naming all types If we're going to go down the same route, we need a solution. Clearly in some cases |
|
@glaebhoerl good point. could mod io {
#[rustdoc_name="io::Error"]
/// <description of io::Error>
enum Error {
...
}
...
} |
|
That was my first thought as well. A more automated approach which just occurred to me might be that if a given page contains references to more than one item with the same name, prepend path components to each until they differ. That would probably help in a lot of cases, though there may still be ones where, while only one is referenced, more than one exists in the wider universe, and it's not obvious which of them is referred to. But even in that case, if the reader knows about the rule, she could infer that all references on the page are to the same item, and by hovering one of them discover which it is, which is less bad than having to hover all of them in case one of them is different. (Perhaps there could also be more sophisticated approaches like checking whether more than one item with that name exists in the entire crate-it's-from or crate-being-documented.) |
|
+1 For the automatic approach. I would love it if the exact algorithm takes account of both what's on the page and what's in the crate. Consider that it will probably be common to libraries to make |
|
The |
* `Ed25519PublicKey` => `ed25519::PublicKey` * `Ed25519Signature` => `ed25519::Signature` * `EcdsaPublicKey` => `ecdsa::PublicKey` * `EcdsaSignature` => `ecdsa::Signature` These names were prefixed with the signature type previously to avoid clashes with traits of the same name (i.e. `signatory::PublicKey` and `signatory::Signature`) however the namespace clash can also be avoided by using the `ed25519` and `ecdsa` module names. This is considered a Rust best practice: rust-lang/rfcs#356
* `Ed25519PublicKey` => `ed25519::PublicKey` * `Ed25519Signature` => `ed25519::Signature` * `EcdsaPublicKey` => `ecdsa::PublicKey` * `EcdsaSignature` => `ecdsa::Signature` These names were prefixed with the signature type previously to avoid clashes with traits of the same name (i.e. `signatory::PublicKey` and `signatory::Signature`) however the namespace clash can also be avoided by using the `ed25519` and `ecdsa` module names. This is considered a Rust best practice: rust-lang/rfcs#356
- Move generate methods to the appropriate static types - Remove redundant name prefixes (Rust [RFC#356]) [RFC#356]: rust-lang/rfcs#356
To follow this convention RFC: rust-lang/rfcs#356
To follow this convention RFC: rust-lang/rfcs#356
This commit contains a pretty extensive refactor of the advisory types, which is backwards compatible with most of the existing fields (and ignores minor, old ones that have changed). Notably, support for the (poorly utilized) `affected_*` fields is dropped, and replaced with a new restructured `[affected]` section. Support for a `[versions]` section has also been added, which can coexist with the existing `patched_versions` and `unaffected_versions` fields. The accessors for the legacy fields are no-longer `pub`, and the new fields in the `Advisory` struct are automatically populated from the old ones if the new ones are empty (both can be populated, but only if they match, otherwise it's considered a parse error). The migration plan would be to wait until most users have the new version of the parser (say, 6 mo - 1 yr), and then switch all of the advisories over wholesale. Additionally this removes redundant prefixing on type names which repeat their module name, which is considered a Rust best practice: rust-lang/rfcs#356 The following have been renamed: - `advisory::id::AdvisoryId` => `advisory::id::Id` - `db::AdvisoryDatabase` => `db::Database` - `package::PackageName` -> `package::Name` Finally, this adds some example advisories, using the legacy `patched_versions` and `unaffected_versions` (a.k.a. "V1") and the new `[versions]` toplevel section to ensure the parser is compatible with both the old and new formats.
This is a conventions RFC that proposes that the items exported from a module
should never be prefixed with that module name. For example, we should have
io::Error, notio::IoError.(An alternative design is included that special-cases overlap with the
prelude.)Rendered