I've read a few discussions about preludes over the years, and I've gathered that the Rust community is mostly against them (outside of std). I don't think I've ever seen anyone argue that they are always bad, but I'm curious why people don't like them. As far as I can tell there are two main objections:
They pollute the namespace, and may cause name collisions.
Explicitly naming all the imported types makes it much easier to see what crate types originate from.
For my part, the second point is the big one. I often use the imports to see where types come from.
With that said.. I have a couple of crates that I use frequently that have a few different types that in some sense belong together -- meaning that once I pull one in, I end up pulling one or more other types in as well. I sometimes end up with pretty long use-lines to do all the manual imports.
I was wondering if there was a way to have something between use somecrate::prelude::* and use somecrate::{Foo, Bar, Baz, SomethingMore, SomethingElse, EvenMore}. Like mini-preludes, that have a finer level of granularity (i.e. less namespace pollution).
Something akin to use somecrate::features::client::*, use somecrate::features::server::*.
Is this pattern in use anywhere? How frowned upon is it?
Most of the issues come from glob imports, not from preludes per-se. There's nothing especially problematic about having a somecrate::features::client that you import from, so that you do use somecrate::features::client::{Foo, Bar, Baz, SomethingMore, SomethingElse, EvenMore};, but glob imports can cause "interesting" issues.
There's two problems with glob imports:
It makes it harder to track down where a given import comes from, especially once I have two or more glob imports in my module.
Unlike explicit imports, which error if there's a name conflict, glob imports simply hide the thing that conflicts, delaying the ambiguity issue until first use.
For the first, consider two crates that both export a SocketOptions for their server-side. I do use somecrate::client::*; and use othercrate::server::*;, but because I don't realise that SocketOptions is a server-side only thing, and I'm not that familiar with either crate, I assume that SocketOptions has full path ::somecrate::client::SocketOptions. This leads to confusion in things like code review until I realise that my assumption is wrong, and that it's othercrate::server::SocketOptions.
And this just gets worse if instead of Foo being a struct, it's an extension trait with a method of the same name for all three variants; I think I'm using c1::Foo's extension method, but I'm actually using c3::Foo's extension method, and getting surprised when it doesn't behave the way c1::Foo is documented.