"Hello, World! 🎉" in Rust
Node
Rust
In this guide we’ll provide instructions on how to set up a
Rust
project and build a simple data streaming app. By the end of this
tutorial, you’ll be able to send a message to Fluvio and receive it
back.
Prerequisites
Before starting on this tutorial, you’ll need to have completed the following
- Install the Rust programming language
- Have the Fluvio CLI (version
0.7.0
or greater) installed [1]
- Have access to a Fluvio cluster.
See our getting started guide for more details on getting set up.
Create a Topic using the Fluvio CLI
In Fluvio, we send all of our messages to something called a Topic, which
is like a category for related messages. For this tutorial, we’ll create
a topic called hello-fluvio
using the following command:
$ fluvio topic create hello-fluvio
Creating a new Cargo project
We’ll be using Rust’s package manager Cargo to set up our project. Cargo
is helpful because it manages compiling our code and dependencies.
To create a new Rust project with cargo, run
$ cargo new --bin hello-fluvio
Created binary (application) `hello-fluvio` package
One of the first things we need to do is add our dependencies. We’re going to need
the fluvio
crate as well as async-std
because the Fluvio client is async.
In your project folder, edit your Cargo.toml
and add lines under your [dependencies]
section to import fluvio
and async-std
. It should look something like this:
$ cd hello-fluvio
$ cat Cargo.toml
[package]
name = "hello-fluvio"
version = "0.1.0"
authors = ["Your name <your_email@example.com>"]
edition = "2018"
[dependencies]
fluvio = "0.5.0"
async-std = { version = "1.0.0", features = ["attributes"] }
Create Producer/Consumer
Now let’s head on over to the src/main.rs
file. This is where your main
function
lives. Our “Hello World” will be composed of two halves: a producer function, and a
consumer function. Let’s add those right next to our main
function:
use fluvio::FluvioError;
#[async_std::main]
async fn main() {
println!("Hello, world!");
}
async fn produce(message: &str) -> Result<(), FluvioError> {
todo!()
}
async fn consume() -> Result<(), FluvioError> {
todo!()
}
Notice that our new functions start with the async
keyword. We need this because the
Fluvio client library is built with asynchronous code. If you’re curious about how async
code in Rust works, check out the Async Rust book!
Run the following command from within your project
directory
In your consumer window, you should see a message with Hello, world!
appear!
Ignore the warnings, we’ll fix those soon.
We’ll start out by writing our producer code, which will send messages
to our Topic.
async fn produce(message: &str) -> Result<(), FluvioError> {
let producer = fluvio::producer("hello-fluvio").await?;
producer.send_record(message, 0).await?;
Ok(())
}
That’s it for the producer! Let’s hook up some code in main
to call it and test it out.
#[async_std::main]
async fn main() {
let _result = produce("Hello, Fluvio!").await;
}
Notice that we had to use the block_on
function from async_std
. This block_on
function
is acting as our executor, and is part of the machinery that makes async code in Rust work.
We can now run this code and see it in action. We’ll use the fluvio
CLI to see
the message arrive at the “hello-fluvio” topic.
In one terminal window, run the following command to print out events in the “hello-fluvio”
topic
$ fluvio consume hello-fluvio -B
Then in another terminal window, run the following command from within your project
directory
In your consumer window, you should see a message with Hello, Fluvio!
appear!
Now let’s write some code in Rust to do the consuming for us.
use fluvio::Offset;
use async_std::stream::StreamExt;
async fn consume() -> Result<(), FluvioError> {
let consumer = fluvio::consumer("hello-fluvio", 0).await?;
let mut stream = consumer.stream(Offset::beginning()).await?;
// Iterate over all events in the topic
while let Some(Ok(record)) = stream.next().await {
let string = String::from_utf8_lossy(&record.as_ref());
println!("Got record: {}", string);
}
Ok(())
}
This consumer opens an async stream and listens for new events to appear on
the hello-world
topic. When we run it, it will print out every message
ever sent to the topic because we told it to start reading from the
Offset::beginning()
, or the beginning of the topic.
Let’s hook up our consumer code into our main function. We don’t want to run
both the producer and the consumer at the same time, so let’s set up some simple
command-line arguments so we can choose whether to run the producer or the consumer.
#[async_std::main]
async fn main() {
// Collect our arguments into a slice of &str
let args: Vec<String> = std::env::args().collect();
let args_slice: Vec<&str> = args.iter().map(|s| &**s).collect();
let result = match &*args_slice {
[_, "produce"] => {
produce("Hello, Fluvio!").await
},
[_, "consume"] => {
consume().await
},
_ => {
println!("Usage: hello-fluvio [produce|consume]");
return;
},
};
if let Err(err) = result {
println!("Got error: {}", err);
}
}
Now we can run cargo run -- produce
to send messages, or cargo run -- consume
to read them back. Let’s try out our consumer code now:
$ cargo run -- consume
Hello, Fluvio!
In another terminal window, let’s run your producer one more time
You should see another Hello, Fluvio!
message appear in your consumer window!
You’ve successfully communicated messages between two processes by streaming
them with Fluvio.
Bonus: Send your own messages
Let’s do something fun and send custom messages with our producer!
We can make the producer send any text that was typed after the produce
command like this:
#[async_std::main]
async fn main() {
// Collect our arguments into a slice of &str
let args: Vec<String> = std::env::args().collect();
let args_slice: Vec<&str> = args.iter().map(|s| &**s).collect();
let result = match &*args_slice {
[_, "produce"] => {
produce("Hello, Fluvio!").await
},
[_, "produce", rest @ ..] => {
let message = rest.join(" ");
produce(&message).await
},
[_, "consume"] => {
consume().await
},
_ => {
println!("Usage: hello-fluvio [produce|consume]");
return;
},
};
if let Err(err) = result {
println!("Got error: {}", err);
}
}
Now you can send whatever messages you like! Let’s try it out
$ cargo run -- produce Hello, World! 🎉
And in your consumer window, you should see it appear!
$ cargo run -- consume
Hello, Fluvio!
Hello, World! 🎉
Congratulations!
You’ve now completed the Fluvio “Hello, World! 🎉” tutorial!
Checkout Fluvio Rust API on docs.rs to learn more about writing your own Fluvio applications in Rust.