Loading page…
Rust walkthroughs
Loading page…
heck::TitleCase and heck::CamelCase for string transformation?heck::TitleCase transforms strings into title case where each word is capitalized with spaces preserved (e.g., "hello_world" becomes "Hello World"), while heck::CamelCase transforms strings into camel case where words are concatenated with each word capitalized except the first (e.g., "hello_world" becomes "helloWorld"). The key distinction is that TitleCase produces human-readable titles with spaces between words, suitable for display in UI labels and headings, whereas CamelCase produces programming-language identifiers suitable for variable names, type names, and API conventions. Both use the same word-splitting logic (recognizing underscores, hyphens, and case transitions), but differ in output formatting: TitleCase preserves word boundaries with spaces while CamelCase removes them, and CamelCase lowercases the first word while TitleCase capitalizes all words.
use heck::ToTitleCase;
fn main() {
// TitleCase: each word capitalized, spaces preserved
println!("{}", "hello_world".to_title_case()); // "Hello World"
println!("{}", "hello-world".to_title_case()); // "Hello World"
println!("{}", "helloWorld".to_title_case()); // "Hello World"
println!("{}", "HELLO_WORLD".to_title_case()); // "Hello World"
println!("{}", "hello_world_example".to_title_case()); // "Hello World Example"
}ToTitleCase capitalizes each word and separates them with spaces.
use heck::ToLowerCamelCase;
fn main() {
// CamelCase (lower camel case): first word lowercase, rest capitalized
println!("{}", "hello_world".to_lower_camel_case()); // "helloWorld"
println!("{}", "hello-world".to_lower_camel_case()); // "helloWorld"
println!("{}", "HelloWorld".to_lower_camel_case()); // "helloWorld"
println!("{}", "HELLO_WORLD".to_lower_camel_case()); // "helloWorld"
println!("{}", "hello_world_example".to_lower_camel_case()); // "helloWorldExample"
}ToLowerCamelCase lowercases the first word and concatenates all words.
use heck::ToUpperCamelCase;
fn main() {
// UpperCamelCase / PascalCase: all words capitalized
println!("{}", "hello_world".to_upper_camel_case()); // "HelloWorld"
println!("{}", "hello-world".to_upper_camel_case()); // "HelloWorld"
println!("{}", "helloWorld".to_upper_camel_case()); // "HelloWorld"
println!("{}", "HELLO_WORLD".to_upper_camel_case()); // "HelloWorld"
println!("{}", "hello_world_example".to_upper_camel_case()); // "HelloWorldExample"
}ToUpperCamelCase capitalizes all words including the first, producing PascalCase.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Both use the same word splitting logic
// Underscore boundaries
println!("Underscore:");
println!(" Title: {}", "one_two_three".to_title_case()); // "One Two Three"
println!(" Camel: {}", "one_two_three".to_lower_camel_case()); // "oneTwoThree"
// Hyphen boundaries
println!("Hyphen:");
println!(" Title: {}", "one-two-three".to_title_case()); // "One Two Three"
println!(" Camel: {}", "one-two-three".to_lower_camel_case());// "oneTwoThree"
// Case transitions (camelCase input)
println!("Case transitions:");
println!(" Title: {}", "oneTwoThree".to_title_case()); // "One Two Three"
println!(" Camel: {}", "oneTwoThree".to_lower_camel_case()); // "oneTwoThree"
// Mixed separators
println!("Mixed:");
println!(" Title: {}", "one_two-threeThree".to_title_case()); // "One Two Three Three"
println!(" Camel: {}", "one_two-threeThree".to_lower_camel_case()); // "oneTwoThreeThree"
}Both transforms use identical word boundary detection; only output differs.
use heck::{ToTitleCase, ToLowerCamelCase, ToUpperCamelCase};
fn main() {
// TitleCase rules:
// 1. Split into words
// 2. Capitalize first letter of each word
// 3. Lowercase remaining letters of each word
// 4. Join with spaces
println!("TitleCase:");
println!(" {}", "HELLO_WORLD".to_title_case()); // "Hello World"
println!(" {}", "hELLO_wORLD".to_title_case()); // "Hello World"
// LowerCamelCase rules:
// 1. Split into words
// 2. Lowercase entire first word
// 3. Capitalize first letter, lowercase rest for subsequent words
// 4. Concatenate (no spaces)
println!("\nLowerCamelCase:");
println!(" {}", "HELLO_WORLD".to_lower_camel_case()); // "helloWorld"
println!(" {}", "hELLO_wORLD".to_lower_camel_case()); // "helloWorld"
// UpperCamelCase rules:
// 1. Split into words
// 2. Capitalize first letter, lowercase rest for ALL words
// 3. Concatenate (no spaces)
println!("\nUpperCamelCase:");
println!(" {}", "HELLO_WORLD".to_upper_camel_case()); // "HelloWorld"
println!(" {}", "hELLO_wORLD".to_upper_camel_case()); // "HelloWorld"
}Each transformation applies specific case rules consistently.
use heck::{TitleCase, CamelCase};
fn main() {
// Use traits directly for custom transformation
let input = "hello_world_example";
// TitleCase trait
let title: String = input.to_title_case();
println!("Title: {}", title);
// CamelCase trait (lower camel case)
let camel: String = input.to_lower_camel_case();
println!("Lower Camel: {}", camel);
// Upper camel case via trait
let pascal: String = input.to_upper_camel_case();
println!("Upper Camel: {}", pascal);
// The traits are in heck::TitleCase and heck::CamelCase
// but typically used via ToTitleCase and ToLowerCamelCase aliases
}Traits can be imported directly for explicit type annotations.
use heck::{ToTitleCase, ToLowerCamelCase};
struct FieldName {
raw: String,
}
impl FieldName {
fn new(raw: impl Into<String>) -> Self {
FieldName { raw: raw.into() }
}
fn to_display(&self) -> String {
// TitleCase for UI display
self.raw.to_title_case()
}
fn to_variable(&self) -> String {
// LowerCamelCase for code generation
self.raw.to_lower_camel_case()
}
fn to_type(&self) -> String {
// UpperCamelCase for type names
self.raw.to_upper_camel_case()
}
}
fn main() {
let field = FieldName::new("user_name");
println!("Display: {}", field.to_display()); // "User Name"
println!("Variable: {}", field.to_variable()); // "userName"
println!("Type: {}", field.to_type()); // "UserName"
}Use different case transforms for different contexts in the same application.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Numbers are treated as word boundaries
println!("Numbers:");
println!(" Title: {}", "version2_update".to_title_case()); // "Version 2 Update"
println!(" Camel: {}", "version2_update".to_lower_camel_case()); // "version2Update"
println!(" Title: {}", "html5_parser".to_title_case()); // "Html 5 Parser"
println!(" Camel: {}", "html5_parser".to_lower_camel_case()); // "html5Parser"
// Adjacent numbers
println!(" Title: {}", "test123value".to_title_case()); // "Test 123 Value"
println!(" Camel: {}", "test123value".to_lower_camel_case()); // "test123Value"
}Numbers create word boundaries, producing separate words in TitleCase.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Acronyms are detected as word boundaries
println!("Acronyms:");
// All-caps words
println!(" Title: {}", "API_VERSION".to_title_case()); // "Api Version"
println!(" Camel: {}", "API_VERSION".to_lower_camel_case()); // "apiVersion"
// Embedded acronyms
println!(" Title: {}", "parse_URL_string".to_title_case()); // "Parse Url String"
println!(" Camel: {}", "parse_URL_string".to_lower_camel_case()); // "parseUrlString"
// Multi-letter acronyms
println!(" Title: {}", "HTTP_response_code".to_title_case()); // "Http Response Code"
println!(" Camel: {}", "HTTP_response_code".to_lower_camel_case()); // "httpResponseCode"
// heck doesn't preserve acronym casing - it normalizes
// If you need "HTTP" preserved, you'd need custom logic
}heck normalizes acronyms to standard title case; it doesn't preserve all-caps.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Empty string
println!("Empty: '{}'", "".to_title_case()); // ""
println!("Empty: '{}'", "".to_lower_camel_case()); // ""
// Single word
println!("Single: '{}'", "hello".to_title_case()); // "Hello"
println!("Single: '{}'", "hello".to_lower_camel_case()); // "hello"
// Single word upper case
println!("Single upper: '{}'", "HELLO".to_title_case()); // "Hello"
println!("Single upper: '{}'", "HELLO".to_lower_camel_case()); // "hello"
// Numbers only
println!("Numbers: '{}'", "123".to_title_case()); // "123"
println!("Numbers: '{}'", "123".to_lower_camel_case()); // "123"
// Already converted
println!("Already title: '{}'", "Hello World".to_title_case()); // "Hello World"
println!("Already camel: '{}'", "helloWorld".to_lower_camel_case()); // "helloWorld"
}Edge cases produce predictable, normalized output.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Unicode characters are preserved
println!("Unicode:");
println!(" Title: {}", "café_menu".to_title_case()); // "Café Menu"
println!(" Camel: {}", "café_menu".to_lower_camel_case()); // "caféMenu"
println!(" Title: {}", "über_cool".to_title_case()); // "Über Cool"
println!(" Camel: {}", "über_cool".to_lower_camel_case()); // "überCool"
// Non-ASCII boundaries
println!(" Title: {}", "日本語_テスト".to_title_case()); // "日本語 テスト"
println!(" Camel: {}", "日本語_テスト".to_lower_camel_case()); // "日本語テスト"
}Unicode characters are preserved; word boundaries are detected correctly.
use heck::{ToTitleCase, ToLowerCamelCase};
fn main() {
// Both operations allocate a new String
// Complexity is O(n) where n is input length
let input = "this_is_a_long_identifier_name";
// TitleCase must insert spaces, so output may be longer
let title = input.to_title_case();
println!("Title length: {} -> {}", input.len(), title.len());
// CamelCase removes separators, so output is shorter or equal
let camel = input.to_lower_camel_case();
println!("Camel length: {} -> {}", input.len(), camel.len());
// Both allocate exactly once for the output
// No intermediate allocations during transformation
}Both transforms are O(n) with single allocation for output.
use heck::{ToTitleCase, ToLowerCamelCase, ToSnakeCase, ToKebabCase};
fn main() {
let input = "HelloWorld";
// Compare all case transformations
println!("Original: {}", input);
println!("TitleCase: {}", input.to_title_case()); // "Hello World"
println!("CamelCase: {}", input.to_lower_camel_case()); // "helloWorld"
println!("SnakeCase: {}", input.to_snake_case()); // "hello_world"
println!("KebabCase: {}", input.to_kebab_case()); // "hello-world"
println!("ShoutySnake: {}", input.to_shouty_snake_case()); // "HELLO_WORLD"
// Round-trip conversions
let original = "hello_world";
let camel = original.to_lower_camel_case();
let back_to_snake = camel.to_snake_case();
println!("\nRound trip: {} -> {} -> {}", original, camel, back_to_snake);
// hello_world -> helloWorld -> hello_world
}heck provides multiple case transformations for different conventions.
use heck::{ToLowerCamelCase, ToUpperCamelCase, ToSnakeCase};
struct Field {
name: String,
field_type: String,
}
impl Field {
fn new(name: &str, field_type: &str) -> Self {
Field {
name: name.to_string(),
field_type: field_type.to_string(),
}
}
fn field_name(&self) -> String {
// LowerCamelCase for struct fields
self.name.to_lower_camel_case()
}
fn method_name(&self) -> String {
// LowerCamelCase for methods
format!("get_{}", self.name.to_snake_case())
}
fn type_name(&self) -> String {
// UpperCamelCase for type names
self.name.to_upper_camel_case()
}
}
fn main() {
let field = Field::new("user_name", "String");
println!("Field name: {}", field.field_name()); // "userName"
println!("Method name: {}", field.method_name()); // "get_user_name"
println!("Type name: {}", field.type_name()); // "UserName"
}Use CamelCase for code generation to match language conventions.
use heck::ToTitleCase;
struct ConfigField {
key: String,
value: String,
}
impl ConfigField {
fn new(key: &str, value: &str) -> Self {
ConfigField {
key: key.to_string(),
value: value.to_string(),
}
}
fn display_name(&self) -> String {
// TitleCase for UI labels
self.key.to_title_case()
}
fn tooltip(&self) -> String {
format!("{}: {}", self.display_name(), self.value)
}
}
fn main() {
let fields = vec![
ConfigField::new("max_connections", "100"),
ConfigField::new("timeout_seconds", "30"),
ConfigField::new("enable_logging", "true"),
];
println!("Configuration:");
for field in &fields {
println!(" {}: {}", field.display_name(), field.value);
}
// Output:
// Configuration:
// Max Connections: 100
// Timeout Seconds: 30
// Enable Logging: true
}Use TitleCase for human-readable UI labels from config keys.
use heck::{ToTitleCase, ToLowerCamelCase, ToSnakeCase};
use serde::Serialize;
#[derive(Serialize)]
struct ApiResponse {
#[serde(rename = "user_name")]
user_name: String,
#[serde(rename = "created_at")]
created_at: String,
}
fn transform_for_display(response: &ApiResponse) -> Vec<(String, String)> {
// Convert snake_case API keys to TitleCase for display
vec![
("User Name".to_string(), response.user_name.clone()),
("Created At".to_string(), response.created_at.clone()),
]
}
fn transform_for_client(response: &ApiResponse) -> serde_json::Value {
// API uses snake_case, frontend expects camelCase
serde_json::json!({
"userName": response.user_name,
"createdAt": response.created_at,
})
}
fn main() {
let response = ApiResponse {
user_name: "alice".to_string(),
created_at: "2024-03-15".to_string(),
};
let display = transform_for_display(&response);
for (key, value) in display {
println!("{}: {}", key, value);
}
let client_json = transform_for_client(&response);
println!("Client JSON: {}", client_json);
}Transform between API conventions (snake_case) and display (TitleCase) or frontend (camelCase).
use heck::{ToTitleCase, ToLowerCamelCase, ToUpperCamelCase, ToSnakeCase, ToKebabCase};
fn main() {
let examples = vec![
"hello_world",
"user_id",
"API_KEY",
"http_request",
"my_variable_name",
];
println!("{:<20} {:<15} {:<15} {:<15} {:<15}",
"Input", "TitleCase", "LowerCamel", "UpperCamel", "SnakeCase");
println!("{}", "-".repeat(80));
for example in examples {
println!(
"{:<20} {:<15} {:<15} {:<15} {:<15}",
example,
example.to_title_case(),
example.to_lower_camel_case(),
example.to_upper_camel_case(),
example.to_snake_case()
);
}
}Output comparison shows the relationship between different case transforms.
Transformation comparison:
| Input | to_title_case() | to_lower_camel_case() | to_upper_camel_case() |
|-------|-------------------|------------------------|------------------------|
| hello_world | "Hello World" | "helloWorld" | "HelloWorld" |
| hello-world | "Hello World" | "helloWorld" | "HelloWorld" |
| helloWorld | "Hello World" | "helloWorld" | "HelloWorld" |
| HELLO_WORLD | "Hello World" | "helloWorld" | "HelloWorld" |
| API_KEY | "Api Key" | "apiKey" | "ApiKey" |
| user_id | "User Id" | "userId" | "UserId" |
Key differences:
| Aspect | TitleCase | CamelCase (Lower) | CamelCase (Upper) |
|--------|-------------|--------------------|--------------------|
| First word | Capitalized | Lowercase | Capitalized |
| Word separator | Space | None (concatenated) | None (concatenated) |
| Output style | Human-readable | Variable names | Type names |
| Space in output | Yes | No | No |
| Use case | UI labels, headings | Variables, methods | Types, classes |
Word boundary detection (shared by all transforms):
| Pattern | Detected boundary |
|---------|------------------|
| _ | Underscore |
| - | Hyphen |
| aA | Lower-to-upper transition |
| 1a, a1 | Number-letter transition |
Key insight: heck::TitleCase and heck::CamelCase use identical word boundary detection but differ fundamentally in output format—TitleCase produces human-readable strings with spaces between words, while CamelCase produces programming-language identifiers with words concatenated. TitleCase capitalizes every word uniformly, making it ideal for display purposes like UI labels, headings, and documentation. LowerCamelCase (via to_lower_camel_case()) lowercases only the first word, following JavaScript/Java variable naming conventions. UpperCamelCase (via to_upper_camel_case()) capitalizes all words equally, following type/class naming conventions across most languages. Neither transformation preserves original acronym casing—"API_KEY" becomes "Api Key" or "apiKey" rather than "API Key" or "APIKey"—a limitation that requires custom logic if acronym preservation is needed. The choice between TitleCase and CamelCase should be guided by the destination context: TitleCase for human-facing strings and CamelCase for code identifiers.