gRPC for Rust: Libraries and Examples
gRPC is a high-performance remote procedure call (RPC) framework developed by Google. It uses Protocol Buffers (protobuf) for data serialization and HTTP/2 for transport. In the Rust ecosystem, the main libraries for working with gRPC are tonic and grpc-rs. In this article, we will take a detailed look at both libraries, their capabilities, and provide practical code examples.
Why gRPC in Rust?
Rust is a systems programming language with an emphasis on memory safety and performance. gRPC in Rust allows you to create:
- High-load microservices
- Real-time streaming services
- Cross-platform client-server applications
- Integration with existing gRPC infrastructure
Main Libraries
1. Tonic — Modern and Recommended Choice
Tonic is an asynchronous gRPC library built on top of tokio and hyper. It is actively maintained by the community and is considered the standard for gRPC in Rust.
Advantages of tonic:
- Full gRPC support (unary, server-side, client-side, and bidirectional streaming)
- Code generation via
tonic-buildbased on.protofiles - Integration with
tokio(async runtime) - Support for TLS, load balancing, and interceptors
- Active community and good documentation
2. grpc-rs — Alternative from PingCAP
grpc-rs is a Rust wrapper over the gRPC Core C library. It is used in the TiKV project. Suitable for cases where maximum compatibility with the original gRPC C stack is required.
Features of grpc-rs:
- Wrapper over gRPC Core (C library)
- Support for all types of gRPC calls
- Less "magic" — more control
- Requires installation of
protocandcmake - Less popular compared to tonic
Recommendation: For most new projects, choose tonic. Use grpc-rs if you need integration with existing gRPC C code.
Setting Up a Project with tonic
Create a new project:
cargo new grpc-examplecd grpc-exampleAdd dependencies to Cargo.toml:
[dependencies]tonic = "0.11"prost = "0.12"tokio = { version = "1", features = ["full"] }
[build-dependencies]tonic-build = "0.11"Create the file proto/hello.proto:
syntax = "proto3";
package hello;
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply);}
message HelloRequest { string name = 1;}
message HelloReply { string message = 1;}Configure the build — create build.rs:
fn main() -> Result<(), Box<dyn std::error::Error>> { tonic_build::compile_protos("proto/hello.proto")?; Ok(())}Server Example with tonic
Create src/server.rs:
use tonic::{transport::Server, Request, Response, Status};use hello::greeter_server::{Greeter, GreeterServer};use hello::{HelloRequest, HelloReply};
pub mod hello { tonic::include_proto!("hello");}
#[derive(Default)]pub struct MyGreeter;
#[tonic::async_trait]impl Greeter for MyGreeter { async fn say_hello( &self, request: Request<HelloRequest>, ) -> Result<Response<HelloReply>, Status> { let name = request.into_inner().name; let reply = HelloReply { Also in library
Cryptography in C++: Encryption and Hashing with OpenSSL and Crypto++
Machine Learning in Go: Overview of Libraries and Frameworks (Golang)
CLI framework for Swift: creating console utilities on iOS/macOS
Working with CSV in Kotlin: Libraries, Parsing, and Data Writing