I want to call a C++ dynamic library (*.so) from Rust, but I don't want to build it from Rust. Like this,
cc::Build::new()
.file("src/foo.cc")
.shared_flag(true)
.compile("libfoo.so");
In some cases, I only need to call several functions, not all the functions. How can I use it?
Before you go further, make sure you have a basic idea of Rust FFI (foreign function interface).
In Rust, it's easy to call C, but hard to call C++.
To call C functions in Rust, you just have to wrap them with extern
, do some basic type casting and sometimes unsafe
.
To call C++ functions, since Rust does not have built-in knowledge of C++ features, you may have to do a lot of manual translation. For example, here is part of the documentation from Rust-Qt:
Many things are directly translated from C++ to Rust:
- Primitive types are mapped to Rust's primitive types (like
bool
) and types provided by libc crate (likelibc::c_int
).- Fixed-size numeric types (e.g
int8_t
orqint8
) are mapped to Rust's fixed size types (e.g.i8
).- Pointers, references and values are mapped to Rust's respective types.
- C++ namespaces are mapped to Rust submodules.
- C++ classes and structs are mapped to Rust structs. This also applies to all instantiations of template classes encountered in the library's API, including template classes of dependencies.
- Free functions are mapped to free functions.
- Class methods are mapped to structs' implementations.
- Destructors are mapped to
Drop
andCppDeletable
implementations.- Function pointer types are mapped to Rust's equivalent representation. Function pointers with references or class values are not supported.
static_cast
anddynamic_cast
are available in Rust through corresponding traits.Names of Rust identifiers are modified according to Rust's naming conventions.
When direct translation is not possible:
- Contents of each include file of the C++ library are placed into a separate submodule.
- Method overloading is emulated with wrapping arguments in a tuple and creating a trait describing tuples acceptable by each method. Methods with default arguments are treated in the same way.
- Single inheritance is translated to
Deref
andDerefMut
implementation, allowing to call base class methods on derived objects. When deref coercions are not enough,static_cast
should be used to convert from derived to base class.- Getter and setter methods are created for each public class field.
Not implemented yet but planned:
- Translate C++
typedef
s to Rust type aliases.- Implement operator traits for structs based on C++ operator methods (issue). Operators are currently exposed as regular functions with
op_
prefix.- Implement Debug and Display traits for structs if applicable methods exist on C++ side.
- Implement iterator traits for collections.
- Subclassing API (issue).
- Provide access to a class's public variables (issue).
- Provide conversion from enums to int and back (used in Qt API).
- Support C++ types nested into template types, like
Class1<T>::Class2
.Not planned to support:
- Advanced template usage, like types with integer template arguments.
- Template partial specializations.
- Template methods and functions.
My suggestion is to wrap your C++ library as a C library, then call it the official FFI way, or use rust-bindgen to automatically do the wrapping.
If you still want to call C++ in Rust, rustcxx seems like a handy tool. (Update: rustcxx has been archived, try CXX instead.)
As to the library linking, it's pretty simple:
/usr/lib
or /usr/local/lib/
, make sure it can be found by ldconfig -p
.LD_LIBRARY_PATH
to specify the path where your library lays when you run cargo
from the CLI.