{"id":11798,"date":"2025-09-06T10:13:43","date_gmt":"2025-09-06T10:13:43","guid":{"rendered":"https:\/\/putridparrot.com\/blog\/?p=11798"},"modified":"2025-09-06T10:13:43","modified_gmt":"2025-09-06T10:13:43","slug":"rust-and-grpc-with-protocol-buffers","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/rust-and-grpc-with-protocol-buffers\/","title":{"rendered":"Rust and gRPC (with Protocol Buffers)"},"content":{"rendered":"<p>Back in 2018 I published a couple of posts around <a href=\"https:\/\/putridparrot.com\/blog\/using-protocol-buffers\/\" target=\"_blank\">Using Protocol Buffers<\/a> and <a href=\"https:\/\/putridparrot.com\/blog\/using-grpc-with-protocol-buffers\/\" target=\"_blank\">Using gRPC with Protocol Buffers<\/a>. <\/p>\n<p>For this post we&#8217;re going to look at using gRPC and Protocol Buffers from Rust.<\/p>\n<p><strong>Getting Started<\/strong><\/p>\n<p>Before we begin to do anything in Rust we&#8217;ll need protoc on our machine, so checkout https:\/\/github.com\/protocolbuffers\/protobuf\/releases for a release. <\/p>\n<p><em>Note: On Windows we can just use <em>winget install protobuf<\/em> then run <em>protoc &#8211;version<\/em> to check it was installed.<\/em><\/p>\n<p>Also ensure protoc.exe is in your path or set-up via your development tools &#8211; in my case I&#8217;m using JetBrains RustRover and added the environment variable PROTOC to the project configuration with a value of C:\\Users\\{your-username}\\AppData\\Local\\Microsoft\\WinGet\\Links\\protoc.exe as I installed on Windows via winget.<\/p>\n<p>Next, let&#8217;s create the bare bones project. <\/p>\n<ul>\n<li>Create yourself a folder for your project then&#8230;<\/li>\n<li>Run the following (replace rust_grpc) with your project name\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\ncargo new rust_grpc\r\n<\/pre>\n<\/li>\n<li>cd into the folder just created<\/li>\n<li>Update the Cargo.toml to include the following dependencies and build-dependencies\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;dependencies]\r\ntonic = &quot;0.14.2&quot;\r\ntokio = { version = &quot;1&quot;, features = &#x5B;&quot;full&quot;] }\r\nprost = &quot;0.14.1&quot;\r\ntonic-prost = &quot;0.14.2&quot;\r\n\r\n&#x5B;build-dependencies]\r\ntonic-prost-build = &quot;0.14.2&quot;\r\n<\/pre>\n<\/li>\n<\/ul>\n<p><em>Obviously change the dependencies versions to suit.<\/em><\/p>\n<p>The build-dependencies will generate the source code from our .proto file.<\/p>\n<p><strong>Creating the proto file(s)<\/strong><\/p>\n<p>Let&#8217;s create a simple proto file.<\/p>\n<ul>\n<li>Create a folder, we&#8217;ll use a standard name, so ours is called <em>proto<\/em> off of the root folder<\/li>\n<li>Create a file names hello.proto and copy the code below into it (this is a sort of &#8220;Hello World&#8221; of proto files)\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nsyntax = &quot;proto3&quot;;\r\n\r\npackage hello;\r\n\r\nservice Greeter {\r\n  rpc SayHello (HelloRequest) returns (HelloReply);\r\n}\r\n\r\nmessage HelloRequest {\r\n  string name = 1;\r\n}\r\n\r\nmessage HelloReply {\r\n  string message = 1;\r\n}\r\n<\/pre>\n<\/li>\n<li>To generate the code from the .proto, create a build.rs file with the following code\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nuse tonic_prost_build::configure;\r\n\r\nfn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {\r\n    configure()\r\n        .out_dir(&quot;src\/generated&quot;)\r\n        .compile_protos(&amp;&#x5B;&quot;proto\/hello.proto&quot;], &amp;&#x5B;&quot;proto&quot;])\r\n        .unwrap();\r\n    Ok(())\r\n}\r\n<\/pre>\n<\/ul>\n<p>I had problems getting the build.rs to generate source for the proto file, so you might need to create a folder \/src\/generated before running the command and the proto folder is off on the project root i.e. alongside the src folder as mentioned previous, so ensure that&#8217;s correct.<\/p>\n<p>To generate the source files for the project we can run the build from a tool such as RustRover or use <em>cargo build<\/em> from your project folder.<\/p>\n<p>I&#8217;m not going to include the whole file that&#8217;s generated but you should see bits like the following<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n\/\/\/ Generated client implementations.\r\npub struct HelloRequest {\r\n    #&#x5B;prost(string, tag = &quot;1&quot;)]\r\n    pub name: ::prost::alloc::string::String,\r\n}\r\n#&#x5B;derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]\r\npub struct HelloReply {\r\n    #&#x5B;prost(string, tag = &quot;1&quot;)]\r\n    pub message: ::prost::alloc::string::String,\r\n}\r\npub mod greeter_client {\r\n    #!&#x5B;allow(\r\n        unused_variables,\r\n        dead_code,\r\n        missing_docs,\r\n        clippy::wildcard_imports,\r\n        clippy::let_unit_value,\r\n    )]\r\n<\/pre>\n<p>As you can see we have representations of the request and reply from the .proto file.<\/p>\n<p>I also added a mod.rs file to the src\/generated folder which looks like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\npub mod hello;\r\n<\/pre>\n<p>This will make our generated source available to the main.rs file for importing.<\/p>\n<p><em>This example exists on the tonic GitHub repo https:\/\/github.com\/hyperium\/tonic\/tree\/master\/examples\/src\/helloworld, I hadn&#8217;t realised when I started this but I would suggest you check out their examples.<\/em><\/p>\n<p>I&#8217;m going to place everything in the main.rs file for simplicity, but ofcourse the code should be split into client, server and main code when using in anything other than such a simple example, but let&#8217;s look at each section of code separately&#8230;<\/p>\n<p>We have a GreeterSever generated from our proto code but we need to create the equivalent of an &#8220;endpoint&#8221; or &#8220;service&#8221;, so we&#8217;ll create service with the following code<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n#&#x5B;derive(Default)]\r\npub struct GreeterService {}\r\n\r\n#&#x5B;tonic::async_trait]\r\nimpl Greeter for GreeterService {\r\n    async fn say_hello(\r\n        &amp;self,\r\n        request: Request&lt;HelloRequest&gt;,\r\n    ) -&gt; Result&lt;Response&lt;HelloReply&gt;, Status&gt; {\r\n        let name = request.into_inner().name;\r\n        let reply = HelloReply {\r\n            message: format!(&quot;Hello, {}!&quot;, name),\r\n        };\r\n        Ok(Response::new(reply))\r\n    }\r\n}\r\n<\/pre>\n<p>This essentially responds to a <em>HelloRequest<\/em> returning a <em>HelloReply<\/em> &#8211; as mentioned, think of this as your service endpoint.<\/p>\n<p>We&#8217;re going to need to create a server, which will look like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nasync fn grpc_server() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {\r\n    let addr = &quot;&#x5B;::1]:50051&quot;.parse()?;\r\n    let greeter = GreeterService::default();\r\n\r\n    println!(&quot;Server listening on {}&quot;, addr);\r\n\r\n    Server::builder()\r\n        .add_service(GreeterServer::new(greeter))\r\n        .serve(addr)\r\n        .await?;\r\n\r\n    Ok(())\r\n}\r\n<\/pre>\n<p>Notice that we are indeed creating a server, listening on a port. We supply the service to the <em>Server::builder<\/em> via <em>add_service<\/em> and that&#8217;s pretty much it. <\/p>\n<p>Next we&#8217;re going to need a client to send some request, so here&#8217;s an example<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nasync fn grpc_client() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {\r\n\r\n    let mut client = GreeterClient::connect(&quot;http:\/\/&#x5B;::1]:50051&quot;).await?;\r\n\r\n    let request = Request::new(HelloRequest {\r\n        name: &quot;PutridParrot&quot;.into(),\r\n    });\r\n    let response = client.say_hello(request).await?;\r\n\r\n    println!(&quot;Response is {:?}&quot;, response.into_inner().message);\r\n    Ok(())\r\n}\r\n<\/pre>\n<p>Ofcourse the client connects to the server, creates a request and sends it to the server via the <em>say_hello<\/em> function. This is a call via the generated code, not to be confused with the GreeterService function of the same name, however ofcourse this will then go via the wire to the server and be handled by the GreeterService&#8217;s <em>say_hello<\/em> function.<\/p>\n<p>We await the response and println! it.<\/p>\n<p>Now let&#8217;s just create a simple main\/entry point to run the server then run the client and get a response (again this is made simple just for ease of using the one file (main.rs) and ofcourse should be separated in a real world use.<\/p>\n<p><em>Note: I&#8217;ll also include all the use code as well in this sample<\/em><\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nmod generated;\r\n\r\nuse tokio::spawn;\r\nuse tokio::time::{sleep, Duration};\r\nuse tonic::{Request, Response, Status};\r\nuse tonic::transport::Server;\r\nuse crate::generated::hello::greeter_client::GreeterClient;\r\nuse crate::generated::hello::greeter_server::{Greeter, GreeterServer};\r\nuse crate::generated::hello::{HelloReply, HelloRequest};\r\n\r\n#&#x5B;tokio::main]\r\nasync fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {\r\n    spawn(async {\r\n        grpc_server().await.unwrap();\r\n    });\r\n\r\n    sleep(Duration::from_millis(500)).await;\r\n\r\n    grpc_client().await?;\r\n    \r\n    Ok(())\r\n}\r\n<\/pre>\n<p>Use <em>cargo run<\/em> or run via RustRover or your preferred development tools and you should see<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nServer listening on &#x5B;::1]:50051\r\nResponse is &quot;Hello, PutridParrot!&quot;\r\n<\/pre>\n<p><strong>Code<\/strong><\/p>\n<p>Code is available for <a href=\"https:\/\/github.com\/putridparrot\/blog-projects\/tree\/master\/rust_grpc\/rust_grpc\" target=\"_blank\">GitHub<\/a>. Don&#8217;t forget to install protoc and ensure the path is set if you wish to run the code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Back in 2018 I published a couple of posts around Using Protocol Buffers and Using gRPC with Protocol Buffers. For this post we&#8217;re going to look at using gRPC and Protocol Buffers from Rust. Getting Started Before we begin to do anything in Rust we&#8217;ll need protoc on our machine, so checkout https:\/\/github.com\/protocolbuffers\/protobuf\/releases for a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[221,220,191],"tags":[],"class_list":["post-11798","post","type-post","status-publish","format-standard","hentry","category-grpc","category-protocol-buffers","category-rust"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=11798"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11798\/revisions"}],"predecessor-version":[{"id":11805,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/11798\/revisions\/11805"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=11798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=11798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=11798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}