Definition of public API

The API guidelines state here:

A crate cannot be stable (>=1.0.0) without all of its public dependencies being stable.
Public dependencies are crates from which types are used in the public API of the current crate.

So now I am wondering what exactly is the definition of "types are used in the public API of the current crate".

For example if I have the following function:

use rand::Rng;

pub fn my_function<R: Rng>(r: &mut R) { ... }

is Rng considered used in the public API and therefore my crate cannot be stable (since rand is not stable)?

What if I define my own Rng and do an auto-implementation

use rand::Rng;

pub trait MyRng { ... }

impl<R: Rng> MyRng for R { ... }

pub fn my_function<R: MyRng>(r: &mut R) { ... }

Would rand::Rng now be part of my public API or not (the auto-implementation uses it and therefore it shows up in the documentation).

Is there anywhere a good definition when a type is considered part of the public API and when not?

Yes, the rand crate is in your public API in both cases. Think of it like this. Let's say that your crate is using rand 0.8 and I'm also using 0.8, and I write this code:

my_function(&mut thread_rng());

That code compiles just fine.

Next, you upgrade your library from rand 0.8 to 0.9. That breaks my code! Because I have a rand 0.8 rng but I need a rand 0.9. Because you broke users by upgrading the library, it's in your public API.

6 Likes

You can mitigate this a bit with

pub use rand;

in your crate, and instruct users to use yourcrate::rand instead of their rand version. It still can break if users don't adhere to that advice.

2 Likes

Also, it can be quite hard for users to keep this sort of thing straight if they incorporate several crates that all use this trick for the same dependency. If crate_a and crate_b both re-export rand 0.8, users should maintain separate RNGs for interfacing with each crate, but there's nothing forcing them to do so.

If any cross-contamination happens, though, either crate moving to rand 0.9 will break their code.

There’s also an unstable feature to audit your code after you declare which dependencies you intend to be public; see exported-private-dependencies. It has some false positives in particular cases of (IIRC) multiple levels of re-exports, but other than that, it does give you a reasonable machine-checked definition.

2 Likes