If you are happy to provide clients with source code, then providing access to a Git repo or private registry is a good option. An even simpler one is just to provide a source tarball download!
If you want to provide a compiled binary and no source, it becomes a lot more complicated.
I've only started doing some cursory research for something similar (providing a Rust API to a closed-source application), but from what I've found so far:
- If you can dictate the exact compilation environment (e.g "use this exact patch release of the Rust toolchain on this platform with version x of y, ..." or "must compile using this Docker container" etc), then you can use binaries straight from rustc. This is fairly common with C++ application SDKs (where you need to use very specific compilers to produce plugins), but probably wouldn't work so well for providing a library
- Providing a C shared library is the "safest" option - in that it's a very well known approach. Constricting the API through the lowest-common-denominator of C data types does lose a lot of the Rust niceties, but using things like cbindgen to create the shared library, and bindgen to generate the "client-side" bindings should help a bit (at the very least make it less tedious!). This has the advantage that you could also provide bindings in, say, C++ or Python with less effort
- The other "safe" approach might be to instead have the library be a executable and communicate with it via some IPC method (e.g communicate via stdio, or expose a gRPC interface, etc). Excellent for some particular cases, but somewhere between impractical and impossible for others
- It's possible that using Web Assembly as a platform-agnostic "binary" format might work for some cases, but you still have the same "funnel everything through restricted API" problem of the C bindings, and wasm is a lot more "experimental" than a
.dll. E.g something like providing a .wasm file and have the client side run it using wasmer is an interesting approach, but probably quite niche applicability
I found this article quite useful - it has a better explanation of most of the above points. The article is approaching things from the opposite direction as you, but the problem of providing a closed-source library is essentially identical.