Note: This bug applies to all types without a move constructor for which we define struct rust::IsRelocatable<T> : ::std::true_type {}. Currently, that's QFont, QMessageLogContext, QCommandLineOption, and QCommandLineParser, all of which except for QMessageLogContext manage memory. I made a potential fix for this issue in #1448, but I realized it should probably also be documented as an issue, especially because it might come up again in the future.
When cxx passes data by value from Rust into C++, it needs to make sure ownership transfers as well–in other words, that Rust code will not call the destructor on the value, and C++ code eventually will.
On the Rust side, it does so by wrapping the value in MaybeUninit, which prevents the Drop implementation from triggering:
pub fn qdatetime_set_date(datetime: &mut QDateTime, date: QDate) {
unsafe extern "C" {
#[link_name = "rust$cxxqtlib1$cxxbridge1$192$qdatetime_set_date"]
fn __qdatetime_set_date(datetime: *mut ::cxx::core::ffi::c_void, date: *mut QDate);
}
unsafe {
let mut date = ::cxx::core::mem::MaybeUninit::new(date);
__qdatetime_set_date(
::cxx::core::ptr::from_mut::<QDateTime>(datetime).cast::<::cxx::core::ffi::c_void>(),
date.as_mut_ptr(),
);
}
}On the C++ side, it does so by calling std::move, which acquires ownership of the value's resources:
void rust$cxxqtlib1$cxxbridge1$192$qdatetime_set_date(::QDateTime &datetime, ::QDate *date) noexcept {
void (*qdatetime_set_date$)(::QDateTime &, ::QDate) = ::rust::cxxqtlib1::qdatetimeSetDate;
qdatetime_set_date$(datetime, ::std::move(*date));
}However, if the type has no move constructor, the C++ code will instead invoke a copy constructor. That means neither the Rust side nor the C++ side ever call the value's destructor. In the case of a reference-counted type such as QFont, that means the reference counter is not decremented, which means its memory will never be freed.
One approach to this issue is to remove those IsRelocatable overrides, which is what I did in #1448. That will trigger a compile-time error if attempting to pass those types by value into C++. Alternatively, all of the functions involving those types could be rewritten to use UniquePtrs instead, which would be a significant breaking change.
Thanks for spotting this, I note that none of those types have tests in
qt_types_standalonehence not noticing the memory leak 😅 I believe this the same problem that QTimeZone has ? For that one we kept things opaque and used a UniquePtr.https://github.com/KDAB/cxx-qt/blob/2cb6df179b37e760878413d480edd623c61cfdd7/crates/cxx-qt-lib/src/core/qtimezone.rs#L68-L73