Type names with or without prefix? Reexport in root or not?


#1

I have two crates: httpbis and grpc, implementations of HTTP/2 and gRPC correspondingly.

These crates have a lot of similar types: error, result, client, server, configuration etc.

One of the hardest problem things in computer science is naming things.

The question is, how these types should be named? With prefix or without? Should types be called

httpbis::Error, httpbis::Result, httpbis::Client, https::Server
grpc::Error, grpc::Result, grpc::Client, grpc::Server

or

Http2Error, Http2Result, Http2Client, Http2Server
GrpcError, GrpcResult, GrpcClient, GrpcServer

?

Is there style guide or best practices or something like that?

I’ve seen that similar libraries use different schemes:
— mongo client: mongodb::Result
— postgres client postgres::Result
— cassandra client CassResult
— redis client: RedisResult

I came from Java, so currently types have long names (e. g. GrpcResult). But as I see, most of the libraries use short names (e. g. hyper, clap or native-tls). So make libraries similar to common rust conventions, I think I need to rename.

And another similar question. So I have Client and Server. Should they be reexported as httpbis::Client and httpbis::Server or be accessible as httpbis::client::Client and httpbis::server::Server? Or should they be accessible by both names as in hyper (hyper::Client and hyper::client::Client)? API exposes tons of other types, e. g. Header or HttpResponse, should all these types be reexported in library root namespace, or live in submodules?


#2

Worth mentioning that std also uses io::Result. In general, I like library::Result more because it gives the user choice of “qualified vs unqualified”. You can’t really use partially qualified names in Java, that’s why prefixes are the only way :frowning:

For the external libraries I prefer flat namespaces. For me as for the user a library is an atomic indivisible thing. Hierarchical structure does not usually add value outside. Inside the library, hierarchy is useful for organizing implementation and privacy. For the user, it’s also useful to be able to glance at the lib.rs and see all the things that the library has.

The std is an interesting exception though: it has to do a lot of very different things, so it can’t use a single namespace. Also namespacing sometimes allows to avoid name clashes (std::collections::vec::Iter, std::collections::hash_set::Iter).


#3

See https://github.com/brson/rust-api-guidelines#organization

  • Prefer smth::Result to SmthResult
    • As the Zen of Python puts it, “Namespaces are one honking great idea – let’s do more of those!”
    • This way, it can be used in all the three ways:
      • As smth::Result
      • As just Result after use smth::Result
      • As SmthResult after use smth::Result as SmthResult
  • Do split functionality into submodules
  • Do re-export (with pub use) the most commonly used functionality under the root namespace of your crate.
    • So, that functionality should be available both in its submodule and in the root namespace
    • Don’t re-export too much; only the most commonly used stuff.

#4

Wow! “Rust API guidelines” is thorough document.

Thanks!