-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 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