tutorial
ROS

The first steps when using Rust with ROS 2

A step-by-step guide to using Rust with ROS 2

tutorial
ROS

As of today, there are only two officially supported programming languages for ROS 2: C++ and Python. There are many other languages supported by the community, usually targeting specific platforms; for example Java for Android. There is however one general purpose language that is starting to get more and more support, not only from ROS users but from the whole programming community: Rust.

Rust is an open-source systems programming language that guarantees memory safety, thread safety, and minimal runtime. In other words, a fast and secure language. It also allows for Object-Oriented style programming, although the implementation is somewhat different from C++ and Python.

In this tutorial you are going to learn how to create a node with a subscriber and a publisher using Rust. Some basic knowledge of Rust programming is expected, as this post will focus mainly on the use of the Rust client library and not the Rust programming language itself. You can find the source code for this tutorial in our GitHub.

The Rust client library: rclrs

As previously stated, the Rust library for ROS 2 is only supported by the community. It does not come included in the standard installation of ROS 2 nor can it be installed using apt. The project can be found in GitHub. Follow the instructions to install the rclrs in your own workspace to start building your ROS 2 Rust node. Additionally, there is a Dockerfile available in the repository that builds an image with rclsrs and the node we are going to create. Skip ahead to using docker.

# Install Rust, e.g. as described in <https://rustup.rs/>
# Install ROS 2 as described in <https://docs.ros.org/en/humble/Installation.html>
# Assuming you installed the minimal version of ROS 2, you need these additional packages:
sudo apt install -y git libclang-dev python3-pip python3-vcstool # libclang-dev is required by bindgen
# Install these plugins for cargo and colcon:
cargo install --debug cargo-ament-build # --debug is faster to install
pip install git+https://github.com/colcon/colcon-cargo.git
pip install git+https://github.com/colcon/colcon-ros-cargo.git

mkdir -p rust_ws/src && cd rust_ws
git clone <https://github.com/ros2-rust/ros2_rust.git> src/ros2_rust
vcs import src < src/ros2_rust/ros2_rust_humble.repos
. /opt/ros/humble/setup.sh
colcon build

language-bash

This tutorial will cover a basic subscriber to a String and a publisher to a UInt8 containing the length of the last String message received. For now, the command ros2 pkg create does not work for creating Rust packages, so we have to use the Rust cargo package manager. Go to the src folder of the rust_ws and run the following command to create the rclrs package.

cargo new string_length_node

language-bash

Inside the folder created, create a main.rs file in the src folder. This will be the executed file. Start by adding the necessary imports.

// Rust imports
use std::sync::{Arc, Mutex};
// rclrs related imports
use rclrs::{Publisher, RclrsError};
use std_msgs::msg::String as StringMsg;
use std_msgs::msg::UInt8 as UInt8Msg;

language-rust

Now define the a node with the following structure.

// Define the struct for the node
struct StringLengthNode {
node: Arc<rclrs::Node>,
_subscription: Arc<rclrs::Subscription<StringMsg>>,
data: Arc<Mutex<Option<StringMsg>>>,
publisher: Arc<rclrs::Publisher<UInt8Msg>>,
}

language-rust

Once the node is defined, the next step is the implementation of the structure. This means, the functions the structure can execute. Start by creating the implementation and the first function which is the new method.

// Define the implementation of the node.
impl StringLengthNode {
// This function is called when creating the node.
fn new(context: &rclrs::Context)-> Result<Self, rclrs::RclrsError>{
let node = rclrs::Node::new(context, "string_length_node")?;
let data = Arc::new(Mutex::new(None));
let data_cb = Arc::clone(&data);
let _subscription = node.create_subscription(
"string_topic", rclrs::QOS_PROFILE_DEFAULT,
move |msg: StringMsg| {
*data_cb.lock().unwrap() = Some(msg);
},
)?;
let publisher = node.create_publisher("string_length", rclrs::QOS_PROFILE_DEFAULT)?;
// Return Ok with the constructed node
Ok(Self{
node,
_subscription,
publisher,
data,
})
}

language-rust

After this function, create a new function called publish that will be used to publish the data. Remember to close the brackets for the impl.

// This function is called when publishing
fn publish(&self) -> Result<(), rclrs::RclrsError> {
// Get the latest data from the subscription
if let Some(s) = &*self.data.lock().unwrap() {
let mut length_msg = UInt8Msg { data: 0 };
length_msg.data = s.data.len() as u8;
self.publisher.publish(length_msg)?;
}
Ok(())
}
} // impl StringLengthNode

language-rust

Finally, create the main function that will be executed when calling the node. There is an important concept here, because of the Rust ownership model, right now a clone of the node is needed to access the data stored in the data attribute. This is an open topic regarding the rclrs wrapper, which makes this library not as easy to use as rclcpp or rclpy.

fn main() -> Result<(), rclrs::RclrsError>{
println!("Hello, world! - String length node.");
// Create the rclrs context.
let context = rclrs::Context::new(std::env::args())?;
// Create a node and a clone. The first one will subscribe and the clone will publish
let string_length_node = Arc::new(StringLengthNode::new(&context)?);
let string_length_publish_node = Arc::clone(&string_length_node);
// Thread for timer to publish
std::thread::spawn(move || -> Result<(), rclrs::RclrsError> {
loop {
use std::time::Duration;
std::thread::sleep(Duration::from_millis(1000));
string_length_publish_node.publish()?;
}
});
// Spin the subscription node
rclrs::spin(Arc::clone(&string_length_node.node))
}

language-rust

Before you compile, add the dependencies in the Cargo.toml file. This file is similar to the CMakeLists.txt file in C++ packages.

[dependencies]
rclrs = "*"
std_msgs = "*"

language-bash

Compile the package inside the rust_ws you’ve created earlier with colcon build. Once it is done, you can run the node like a ROS 2 node: ros2 run string_length_node string_length_node. Once it’s running, publish on the topic string_topic some string and listen to the topic string_length to check that your node is working.

ros2 topic pub /string_topic std_msgs/msg/String "{data: Hello}"
publisher: beginning loop
publishing #1: std_msgs.msg.String(data='Hello')

language-bash

ros2 topic echo /string_length
data: 5
---

language-bash

Huzzah, you’ve got a Rust node working!

Using docker

Download the repository and navigate, using a terminal window, into the folder:

cd tutorials/ros2/rust.

language-bash

Build the Docker image and tag it as humble_rust with the command:

docker build -t humble_rust .

language-bash

Once completed, run the container name humble_rust_tutorial with the node running using:

docker run -it --name humble_rust_tutorial humble_rust ros2 run string_length_node string_length_node

language-bash

Open two new terminal windows and go into the docker container using:

docker exec -it humble_rust_tutorial bash

language-bash

Use one terminal to publish a string:

ros2 topic pub /string_topic std_msgs/msg/String "{data: Hello}"
publisher: beginning loop
publishing #1: std_msgs.msg.String(data='Hello')

language-bash

Use the other to listen to the topic:

ros2 topic echo /string_length
data: 5
---

language-bash

The future belongs to the community (and you!)

The current version of the rclrs library is not as complete as rclcpp and rclpy. The wrappers around the basic rcl is still in the very early stages, and some functionalities are not ready to use. This is however a great moment to contribute in the development of an open source package. If you are fluent in Rust and want to improve rclrs, do not hesitate to read through the issues of the package and help the community.

Additionally, Foxglove has an ever growing community of users that exchange ideas in our community, don’t hesitate to join us there to ask questions and share ideas.

Read more

Start building with Foxglove.

Get started for free