Moved members
Moving values out of variables or fields in Rust is more explicit than it is in
C++. A value that might be moved with nothing left behind needs to be
represented using an Option<Box<T>>
type in Rust, while in C++ it would just
be a std::unique_ptr<T>
.
#include <memory>
void readMailbox(std::unique_ptr<int> &mailbox,
std::mutex mailboxMutex) {
std::lock_guard<std::mutex> guard(mailboxMutex);
if (!mailbox) {
return;
}
int x = *mailbox;
mailbox = nullptr;
// use x
}
#![allow(unused)] fn main() { use std::sync::Arc; use std::sync::Mutex; fn read(mailbox: Arc<Mutex<Option<i32>>>) { let Ok(mut x) = mailbox.lock() else { return; }; let x = x.take(); // use x } }
Additionally, when taking ownership of a value from within a mutable reference,
something has to be left in its place. This can be done using
std::mem::swap
, and many
container-like types have methods for making common ownership-swapping more
ergonomic, like
Option::take
as seen in the earlier example,
Option::replace
or
Vec::swap
.
Deleting moved objects
Another common use of null pointers in modern C++ is as values for the members of moved objects so that the destructor can still safely be called. E.g.,
#include <cstdlib>
#include <cstring>
// widget.h
struct widget_t;
widget_t *alloc_widget();
void free_widget(widget_t*);
void copy_widget(widget_t* dst, widget_t* src);
// widget.cc
class Widget {
widget_t* widget;
public:
Widget() : widget(alloc_widget()) {}
Widget(const Widget &other) : widget(alloc_widget()) {
copy_widget(widget, other.widget);
}
Widget(Widget &&other) : widget(other.widget) {
other.widget = nullptr;
}
~Widget() {
free_widget(widget);
}
};
Rust's notion of moving objects does not involve leaving behind an object on which a destructor will be called, and so this use of null does not have a corresponding idiom. See the chapter on copy and move constructors for more details.
Click here to leave us feedback about this page.