A hands-on guide to Google's Rust conventions with code comparisons, rationale, and integration tips
Google maintains an internal Rust style guide that builds upon Rust's official conventions while adding Google-specific requirements for consistency across their massive codebase. This guide provides practical examples for each rule, explains the reasoning, and shows how to integrate these standards into your projects.
Who this is for:
What you'll learn:
| Rule | Standard Rust | Google Rust | Severity |
|---|---|---|---|
| Formatting | rustfmt | rustfmt + custom | 🟢 Must |
| Linting | clippy::default | clippy + custom | 🟢 Must |
| Naming | snake_case, PascalCase | snake_case + Google conventions | 🟢 Must |
| Line length | 100 chars | 100 chars | 🟢 Must |
| Comments | //, /// | //, ///, /* */ for all | 🟡 Should |
| Imports | Grouped | Sorted + grouped | 🟢 Must |
| Error handling | anyhow/thiserror | Custom error types | 🟢 Must |
| Testing | test crate | gtest style | 🟢 Must |
| Visibility | pub use | Explicit re-exports | 🟢 Must |
| Macro usage | Encouraged | Restricted | 🟠 Consider |
Google enforces a hard 100-character limit (vs. Rust's default 100 which is a warning).
// ❌ BAD - Exceeds 100 chars
fn process_user_request_and_validate_input(user_id: u64, request: &HttpRequest, validation_rules: &[ValidationRule]) -> Result<Response, Error> {}
// ✅ GOOD - Within limit
fn process_user_request(
user_id: u64,
request: &HttpRequest,
validation_rules: &[ValidationRule],
) -> Result<Response, Error> {
}
// ✅ GOOD - Alternate: use meaningful names
fn process_request(user: &User, request: &HttpRequest) -> Result<Response, Error> {
}
Rationale: Consistency across Google's codebase and easier code review on standard terminals.
// ❌ BAD - Tabs
fn main() {
\tlet x = 5;
\tprintln!("{}", x);
}
// ✅ GOOD - 4 spaces
fn main() {
let x = 5;
println!("{}", x);
}
Tooling: rustfmt handles this automatically with default config.
Google requires opening braces on the same line (K&R style).
// ❌ BAD - Allman style
fn foo()
{
// ...
}
// ✅ GOOD - K&R style
fn foo() {
// ...
}
// ✅ GOOD - For control flow
if condition {
// ...
} else {
// ...
}
Note: This is rustfmt's default, so no special config needed.
// ❌ BAD
let nums = [1, 2, 3,];
// ✅ GOOD
let nums = [1, 2, 3];
// ✅ GOOD - Multi-line: keep trailing commas
let nums = [
1,
2,
3,
];
Tooling: rustfmt Option: trailing_comma = "Vertical"
// ✅ GOOD
pub struct HttpRequest;
pub enum StatusCode;
pub trait Serializer;
// ❌ BAD
pub struct http_request; // Should be PascalCase
pub enum status_code; // Should be PascalCase
// ✅ GOOD
let user_name = "alice";
let http_request = HttpRequest::new();
fn process_request() {}
// ❌ BAD
let UserName = "alice"; // Should be snake_case
fn ProcessRequest() {} // Should be snake_case
// ✅ GOOD
const MAX_CONNECTIONS: u32 = 100;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
// ❌ BAD
const MaxConnections = 100; // Should be SCREAMING_SNAKE_CASE
const default_timeout = 30; // Should be SCREAMING_SNAKE_CASE
Google treats acronyms as words in identifiers (unlike some style guides that capitalize them).
// ✅ GOOD - Google style
let http_server = HttpServer::new(); // "http" as word
let url_path = extract_url_path(); // "url" as word
let json_data = parse_json_data(); // "json" as word
// ⚠️ ACCEPTABLE but not Google-preferred
let HTTPServer = HttpServer::new(); // All-caps acronym
let URLPath = extract_url_path(); // All-caps acronym
Rationale: Improves readability and consistency with Google's other language style guides.
Google requires imports to be:
// ✅ GOOD
use std::collections::HashMap;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::error::AppError;
use crate::model::User;
use crate::util;
// ❌ BAD - Unsorted within groups
use serde::Deserialize;
use std::time::Duration;
use serde::Serialize;
use std::collections::HashMap;
// ❌ BAD - Wrong grouping
use crate::model::User;
use std::collections::HashMap;
use serde::Serialize;
Tooling: Use rust-import-sort (cargo-sort) with Google config:
# .rustfmt.toml or rust-toolchain
[imports_granularity]
group = "Crate"
preserve_block = false
// ✅ GOOD - Line comments for code explanations
// Check if user is authenticated before processing
if !user.is_authenticated() {
return Err(Unauthorized);
}
// ⚠️ AVOID - Block comments for code
/* Check if user is authenticated */
if !user.is_authenticated() {
return Err(Unauthorized);
}
/// ✅ GOOD - Doc comments for public items
///
/// Represents a user in the system.
///
/// # Examples
///
/// ```
/// let user = User::new("alice");
/// assert_eq!(user.name(), "alice");
/// ```
pub struct User {
name: String,
}
impl User {
/// Creates a new User with the given name.
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
/// Returns the user's name.
pub fn name(&self) -> &str {
&self.name
}
}
// TODO and // FIXME Capitalized// ✅ GOOD
// TODO(bob): Add caching for this expensive operation
// FIXME(alice): Handle the error case properly
// ❌ BAD
// todo: add caching
// fixme: handle error
Rationale: Consistent with Google's C++ and Java style guides, makes searching easier.
Google discourages anyhow and thiserror for library code, preferring explicit error enums.
// ❌ BAD for libraries - anyhow
use anyhow::{Context, Result};
fn process() -> Result<()> {
let file = std::fs::File::open("config.json")
.context("Failed to open config")?;
// ...
}
// ✅ GOOD - Custom error enum
error_chain! {
pub enum AppError {
ForeignKeyViolation(table: String, column: String),
SerializationError(serde_json::Error),
IoError(std::io::Error),
}
}
// Or with thiserror (for binaries, it's more acceptable)
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(#[from] serde_json::Error),
}
Rationale: Better error handling across crate boundaries, explicit error types make APIs clearer.
// ✅ GOOD
#[derive(Debug)]
pub struct ParseError {
line: usize,
column: usize,
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "parse error at line {}, column {}", self.line, self.column)
}
}
// ❌ BAD - Not a full sentence
write!(f, "Line {}, Column {}", self.line, self.column)
// ❌ BAD - Starts with capital
write!(f, "Parse error at line {}, column {}", self.line, self.column)
Google requires explicit re-exports at the crate root for public API.
// ✅ GOOD - lib.rs
pub mod error;
pub mod model;
pub mod util;
// Explicit re-exports for public API
pub use error::AppError;
pub use model::User;
pub use util::Config;
// ❌ BAD - No re-exports, users must navigate modules
pub mod error;
pub mod model;
pub mod util;
Rationale: Clear public API surface, easier to maintain backwards compatibility.
pub(crate) for Internal-to-Crate Items// ✅ GOOD - Helmets module is internal to crate
pub(crate) mod helpers {
pub fn internal_util() {}
}
pub fn public_api() {
helpers::internal_util();
}
// ❌ BAD - Exposing internal details
pub mod helpers { // Now part of public API
pub fn internal_util() {}
}
For large modules, Google recommends a "table of contents" comment at the top.
//! This module provides user management functionality.
//!
//! ## Contents
//!
//! - [`User`]: Represents a user in the system
//! - [`UserRepository`]: Trait for user persistence
//! - [`InMemoryUserRepository`]: In-memory implementation
//! - [`functions`]: Utility functions for user operations
pub struct User;
pub trait UserRepository;
pub struct InMemoryUserRepository;
pub mod functions;
Google has specific guidelines for clarity in ownership patterns.
// ❌ BAD - Unnecessary clone
fn greet(user: User) {
println!("Hello, {}", user.name());
}
// ✅ GOOD - Borrow instead
fn greet(user: &User) {
println!("Hello, {}", user.name());
}
Rationale: Avoids unnecessary allocations and makes ownership explicit.
Rc and Arc Judiciously// ⚠️ CAUTION - Rc/Arc can hide ownership complexity
// Prefer restructuring to avoid shared ownership when possible
// ✅ GOOD - Clear single ownership
struct Container {
item: Item,
}
// ❌ BAD - Shared ownership without clear reason
struct Container {
item: Rc<Item>,
}
Rationale: Shared ownership increases complexity and can lead to reference cycles.
// ✅ GOOD - Lifetime clearly documented
/// Processes a request with the given context.
///
/// # Lifetime
///
/// The `ctx` must outlive the returned `Response`.
pub fn process<'a>(ctx: &'a Context) -> Response<'a> {
// ...
}
// ❌ BAD - Lifetime not documented in complex cases
pub fn process<'a>(ctx: &'a Context) -> Response<'a> {
// ...
}
// ❌ BAD - Pass by value for large struct
fn process(config: Config) {}
// ✅ GOOD - Pass by reference
fn process(config: &Config) {}
// ✅ GOOD - Pass by value for Copy types
fn process(count: usize) {}
Option for Potentially Absent Values// ❌ BAD - Null pattern with default
fn get_user(id: u64) -> User {
// Returns default user if not found - hides absence
User::default()
}
// ✅ GOOD - Explicit absence
fn get_user(id: u64) -> Option<User> {
// ...
}
Result for Fallible Operations// ❌ BAD - Panics on error
fn parse_config(data: &str) -> Config {
serde_json::from_str(data).expect("Valid config");
}
// ✅ GOOD - Returns Result
fn parse_config(data: &str) -> Result<Config, ParseError> {
serde_json::from_str(data).map_err(ParseError::from)
}
dyn Trait for Trait Objects// ✅ GOOD - Explicit dyn
fn process(reader: &dyn Read) -> io::Result<()> {
// ...
}
// ❌ BAD - Implicit dyn (older style)
fn process(reader: &Read) -> io::Result<()> {
// ...
}
Rationale: Explicit dyn makes it clear when using trait objects vs. generics.
// ✅ GOOD - Generic (static dispatch, zero cost)
fn process<T: Read>(reader: &mut T) -> io::Result<()> {
// ...
}
// ⚠️ ACCEPTABLE - Trait object (dynamic dispatch)
fn process(reader: &mut dyn Read) -> io::Result<()> {
// ...
}
Rationale: Generics allow compiler optimizations and have no runtime overhead.
/// A serializer for converting values to bytes.
///
/// # Implementations
///
/// Implementors must ensure that serialization is deterministic
/// and that deserialization produces equivalent values.
pub trait Serializer {
fn serialize(&self, value: &dyn Serialize) -> Vec<u8>;
}
std::sync::Arc for Thread-Safe Reference Countinguse std::sync::Arc;
use std::thread;
// ✅ GOOD - Arc for thread-safe sharing
let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);
thread::spawn(move || {
println!("{:?}", data_clone);
});
// ❌ BAD - Rc in multi-threaded context (not Send/Sync)
use std::rc::Rc;
let data = Rc::new(vec![1, 2, 3]);
// This won't compile with Send types
Mutex Instead of RefCell for Thread-Safe Interior Mutabilityuse std::sync::Mutex;
// ✅ GOOD - Mutex for thread-safe interior mutability
struct SharedState {
data: Mutex<Vec<u8>>,
}
// ❌ BAD - RefCell is not thread-safe
use std::cell::RefCell;
struct SharedState {
data: RefCell<Vec<u8>>, // Single-threaded only
}
/// A thread-safe cache for computed values.
///
/// # Thread Safety
///
/// This cache uses `Arc<Mutex<T>>` internally and is safe to use
/// from multiple threads concurrently.
#[derive(Clone)]
pub struct ThreadSafeCache<T> {
inner: Arc<Mutex<HashMap<String, T>>>,
}
Google discourages macros due to reduced readability and increased compile times.
// ❌ BAD - Macro for simple pattern
macro_rules! vec_push_all {
($vec:expr, $($val:expr),+) => {
$( $vec.push($val); )*
};
}
// ✅ GOOD - Use iterator methods
vec.extend(&[val1, val2, val3]);
// ✅ GOOD - Derive macro
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config;
// ✅ GOOD - Attribute macro for validation
#[validate]
pub struct UserInput;
/// Creates a new error type with the given name.
///
/// # Example
///
/// ```
/// define_error!(MyError, "Something went wrong");
/// ```
///
/// # Expands to
///
/// ```
/// #[derive(Debug)]
/// pub struct MyError;
///
/// impl std::fmt::Display for MyError {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// write!(f, "Something went wrong")
/// }
/// }
/// ```
#[macro_export]
macro_rules! define_error {
($name:ident, $msg:expr) => {
// ...
};
}
// ✅ GOOD - All public items documented
aaa
/// Represents a user in the system.
pub struct User;
/// Creates a new User.
pub fn new_user() -> User;
// ❌ BAD - Missing documentation
pub struct Config; // No doc comment
pub fn create_config() -> Config; // No doc comment
//! for Module-Level Documentation//! This module provides HTTP client functionality.
//!
//! The main entry point is the [`Client`] struct, which can be configured
//! with various options for timeouts, retries, and authentication.
pub struct Client;
/// Parses a configuration file from the given path.
///
/// # Examples
///
/// ```
/// use std::path::Path;
///
/// let config = parse_config(Path::new("config.json"))?;
/// assert_eq!(config.port, 8080);
/// ```
///
/// # Errors
///
/// Returns an error if the file cannot be read or parsed.
pub fn parse_config(path: &Path) -> Result<Config, ConfigError> {
// ...
}
Google uses _test.rs files alongside the module under test.
my_project/
├── src/
│ ├── lib.rs
│ ├── user.rs
│ └── user_test.rs // ✅ GOOD - Google style
│
└── tests/
└── integration_test.rs
// ✅ GOOD - Google-style unit tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_creation() {
let user = User::new("alice");
assert_eq!(user.name(), "alice");
}
#[test]
fn test_user_name_empty() {
let user = User::new("");
assert!(user.name().is_empty());
}
}
// ❌ BAD - Tests outside mod tests
#[test]
fn test_user_creation() {
// ...
}
Rationale: Keeps tests close to the code they test, better organization.
test_<description>// ✅ GOOD
#[test]
fn test_parse_json_valid() {}
#[test]
fn test_parse_json_invalid_utf8() {}
#[test]
fn test_parse_json_empty() {}
// ❌ BAD - Not descriptive
#[test]
fn test1() {}
// ❌ BAD - Missing "test" prefix
#[test]
fn parse_json_valid() {}
Google extends clippy with custom lints. Here are key ones with examples:
clippy::needless_pass_by_value// ❌ BAD - Pass by value when reference is sufficient
fn print_user(user: User) {
println!("{}", user.name());
}
// ✅ GOOD - Pass by reference
fn print_user(user: &User) {
println!("{}", user.name());
}
clippy::unused_self// ❌ BAD - Unused self parameter
impl User {
pub fn is_valid(&self) -> bool {
true // Doesn't use self
}
}
// ✅ GOOD - Remove self if not needed
impl User {
pub fn is_valid() -> bool {
true
}
}
// ✅ GOOGLE PREFERRED - Make it static
impl User {
pub fn is_valid() -> bool {
true
}
}
clippy::inefficient_to_string// ❌ BAD - Inefficient allocation
let s = format!("{}", some_int);
// ✅ GOOD - Use to_string()
let s = some_int.to_string();
unwrap() or expect() in Library Code// ❌ BAD for libraries
let user = users.get(0).unwrap();
let config = Config::from_file("config.yaml").expect("Config must exist");
// ✅ GOOD - Proper error handling
let user = users.get(0).ok_or(AppError::UserNotFound)?;
let config = Config::from_file("config.yaml").map_err(AppError::ConfigLoad)?;
// ⚠️ ACCEPTABLE for binaries (main.rs only)
let config = Config::from_file("config.yaml").expect("Config must exist");
Create or update .rustfmt.toml:
# Google Rust Style Guide - rustfmt Configuration
# Basic formatting
edition = "2021"
fn_single_line = false
brace_style = "SameLineWhere"
tab_spaces = 4
newline_style = "Auto"
# Line length
max_width = 100
wrap_comments = true
comment_width = 100
# Indentation
indent_style = "Block"
hard_tabs = false
# Commas
trailing_comma = "Vertical"
match_block_trailing_comma = true
# Imports
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
# Items
-reorder_impl_items = true
reorder_modules = true
# Struct formatting
struct_lit_single_line = false
struct_variant_width = 35
# Control flow
control_brace_style = "AlwaysSameLine"
where_single_line = false
# Comments
format_code_in_doc_comments = true
license_template_path = "LICENSE.txt"
Create or update clippy.toml or .cargo/config.toml:
[lints.clippy]
# Google: Treat warnings as errors
warnings_as_errors = true
# Enabled lints (Google additions)
adjustable_string_slice = "warn"
allowed_characters = "warn"
as_underscore = "warn"
box_vec = "warn"
cloned_instead_of_copied = "warn"
dbg_macro = "warn"
default_trait_access = "warn"
deref_addrof = "warn"
doc_markdown = "warn"
empty_enum = "warn"
empty_line_after_doc_comments = "warn"
empty_structs_with_brackets = "warn"
eta_redundant_collect = "warn"
excessive_precision = "warn"
filetype_is_file = "warn"
flat_map_option = "warn"
float_cmp_const = "warn"
fn_to_numeric_cast_any = "warn"
format_in_format_args = "warn"
format_push_string = "warn"
function_item_references = "warn"
get_unwrap = "warn"
inconsistent_struct_constructor_formats = "warn"
indexing_slicing = "warn"
inefficient_to_string = "warn"
init_numbered_fields = "warn"
iter_next_slice = "warn"
large_stack_arrays = "warn"
large_types_passed_by_value = "warn"
len_without_is_empty = "warn"
let_underscore_drop = "warn"
let_underscore_must_use = "warn"
let_underscore_untyped = "warn"
linkedlist = "warn"
lossy_float_literal = "warn"
map_err_ignore = "warn"
mem_forget = "warn"
missing_assert_message = "warn"
missing_docs_in_private_items = "warn"
missing_panics_doc = "warn"
mutex_atomic = "warn"
needless_borrow = "warn"
needless_continue = "warn"
needless_pass_by_value = "warn"
needless_range_loop = "warn"
no_mangle_const_items = "warn"
non_ascii_literal = "warn"
option_if_let_else = "warn"
or_fun_call = "warn"
print_stderr = "warn"
print_stdout = "warn"
pub_underscore_fields = "warn"
redundant_allocation = "warn"
redundant_pattern_matching = "warn"
ref_patterns = "warn"
rest_pat_in_fully_bound_structs = "warn"
same_name_method = "warn"
shadow_reuse = "warn"
shadow_same = "warn"
shadow_unrelated = "warn"
single_char_lifetime_names = "warn"
string_to_string = "warn"
temporary_assignment = "warn"
trivially_copy_pass_by_ref = "warn"
type_complexity = "warn"
unconditional_recursion = "warn"
uninlined_format_args = "warn"
unused_labels = "warn"
unused_self = "warn"
useless_conversion = "warn"
# Google: Deny in library code
unwrap_used = "deny"
expect_used = "deny"
panic = "allow" # Allowed in tests/main.rs
[dev-dependencies]
clippy = { version = "0.1", features = ["default"] }
[lints.clippy]
# Inherit from clippy.toml or set directly
warnings_as_errors = true
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
targets = ["x86_64-unknown-linux-gnu"]
[rust]
# Google uses these settings
extern-crate-private = true
.git/hooks/pre-commit or use pre-commit framework:
#!/bin/sh
cargo fmt -- --check
cargo clippy -- -D warnings
cargo test
// ✅ GOOGLE - Explicit new() constructor
pub struct User {
name: String,
email: String,
}
impl User {
pub fn new(name: impl Into<String>, email: impl Into<String>) -> Self {
Self {
name: name.into(),
email: email.into(),
}
}
}
// ⚠️ COMMUNITY - Also acceptable but less explicit
pub struct User {
pub name: String,
pub email: String,
}
Google Rationale: Explicit constructors allow validation, future flexibility, and better API evolution.
// ✅ GOOGLE - Builder for complex construction
#[derive(Default)]
pub struct RequestBuilder {
method: String,
url: String,
headers: Vec<(String, String)>,
body: Option<Vec<u8>>,
}
impl RequestBuilder {
pub fn new(method: impl Into<String>, url: impl Into<String>) -> Self {
Self {
method: method.into(),
url: url.into(),
..Default::default()
}
}
pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.push((key.into(), value.into()));
self
}
pub fn body(mut self, body: Vec<u8>) -> Self {
self.body = Some(body);
self
}
pub fn build(self) -> Request {
Request {
method: self.method,
url: self.url,
headers: self.headers,
body: self.body,
}
}
}
// ✅ GOOGLE - Rich error context
#[derive(Debug)]
pub enum DatabaseError {
ConnectionFailed {
host: String,
port: u16,
source: std::io::Error,
},
QueryFailed {
query: String,
params: Vec<String>,
source: sqlx::Error,
},
NotFound {
table: String,
id: i64,
},
}
impl std::fmt::Display for DatabaseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DatabaseError::ConnectionFailed { host, port, .. } => {
write!(f, "failed to connect to database at {host}:{port}")
}
DatabaseError::QueryFailed { query, .. } => {
write!(f, "query failed: {query}")
}
DatabaseError::NotFound { table, id } => {
write!(f, "{table} with id {id} not found")
}
}
}
}
// Usage
fn get_user(id: i64) -> Result<User, DatabaseError> {
let conn = establish_connection()
.map_err(|source| DatabaseError::ConnectionFailed {
host: "localhost".to_string(),
port: 5432,
source,
})?;
let user = conn.query_one("SELECT * FROM users WHERE id = ?", vec![id.to_string()])
.map_err(|source| DatabaseError::QueryFailed {
query: "SELECT * FROM users WHERE id = ?".to_string(),
params: vec![id.to_string()],
source,
})?;
user.ok_or(DatabaseError::NotFound {
table: "users".to_string(),
id,
})
}
| Aspect | Google Style | Rust Community Style | Recommendation |
|---|---|---|---|
| Formatting | rustfmt + custom | rustfmt default | Use Google config for Google projects |
| Linting | clippy + custom + deny unwrap | clippy default | Use Google lints for libraries |
| Error handling | Custom error types | anyhow/thiserror | Custom for libs, anyhow for apps |
| Testing | _test.rs files | tests/ directory | Use both when appropriate |
| Documentation | Full doc comments | Full doc comments | Always document public API |
| Module structure | Explicit re-exports | Direct imports | Explicit re-exports for public API |
| Imports | Sorted + grouped | Grouped only | Use Google for consistency |
| Line length | Hard 100 | Soft 100 | 100 is good either way |
| Acronyms | lowercase (http) | Mixed (HTTP or http) | Follow project conventions |
##Migration Guide: Adopting Google Style
# Check formatting
cargo fmt -- --check
# Check lints
cargo clippy -- -D warnings
# Check tests
cargo test
# Apply rustfmt
cargo fmt
# Fix clippy issues
cargo clippy -- -D warnings --fix -Z unstable-options
Focus on:
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup component add rustfmt
- run: cargo fmt -- --check
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup component add clippy
- run: cargo clippy -- -D warnings
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cargo test
| Tool | Purpose | Google Config |
|---|---|---|
| rustfmt | Code formatting | .rustfmt.toml provided above |
| clippy | Linting | clippy.toml or Cargo.toml lints |
| cargo-sort | Import sorting | Default works well |
| taplo | TOML formatting | Default config |
| rust-analyzer | IDE support | Respects rustfmt/clippy settings |
Google's Rust Style Guide builds on Rust's strong foundation and adds Google-specific conventions for consistency at scale. The key differences are:
When to use Google Style:
When to adapt:
The guide is practical and opinionated — it values consistency and maintainability over individual preferences, which is exactly what you need when working with large teams and codebases that will live for years.
Want to dive deeper into production-grade Rust? Check out our Advanced Rust: Building Production Systems course on GraphWiz AI.
This hands-on course covers:
Perfect for developers who understand Rust basics and want to master the patterns and practices used by top engineering organizations.
Last updated: October 18, 2026 Google Rust Style Guide: https://google.github.io/styleguide/rustguide.html