What is the purpose of heck::ToLowerCamelCase for converting identifiers to camelCase strings?
ToLowerCamelCase is a trait in the heck crate that converts snake_case or kebab-case identifiers to lowerCamelCase (also known as "dromedaryCase"), where the first word is lowercase and subsequent words are capitalized. This transformation is commonly needed when bridging between Rust's conventional snake_case naming and other languages or contexts that expect camelCase, such as JavaScript APIs, JSON serialization, CSS class names, or database column naming conventions.
What is LowerCamelCase
use heck::ToLowerCamelCase;
fn camel_case_basics() {
// Lower camelCase: first word lowercase, subsequent words capitalized
// "hello_world" -> "helloWorld"
// "my_variable_name" -> "myVariableName"
// Compare with upper CamelCase (PascalCase):
// "hello_world" -> "HelloWorld" (first word also capitalized)
let input = "hello_world";
let camel = input.to_lower_camel_case();
assert_eq!(camel, "helloWorld");
}ToLowerCamelCase produces lowerCamelCase where the first letter is lowercase.
Basic Conversion with ToLowerCamelCase
use heck::ToLowerCamelCase;
fn basic_conversions() {
// Snake_case to lowerCamelCase
assert_eq!("hello_world".to_lower_camel_case(), "helloWorld");
assert_eq!("my_variable_name".to_lower_camel_case(), "myVariableName");
assert_eq!("user_id".to_lower_camel_case(), "userId");
// Single word stays lowercase
assert_eq!("hello".to_lower_camel_case(), "hello");
// Multiple underscores collapse
assert_eq!("hello__world".to_lower_camel_case(), "helloWorld");
// Leading/trailing underscores are removed
assert_eq!("_private_field".to_lower_camel_case(), "privateField");
assert_eq!("trailing_".to_lower_camel_case(), "trailing");
}The trait converts snake_case identifiers by capitalizing subsequent words and removing underscores.
Kebab-case Support
use heck::ToLowerCamelCase;
fn kebab_case() {
// Kebab-case (dash-separated) also converts to camelCase
assert_eq!("my-variable-name".to_lower_camel_case(), "myVariableName");
assert_eq!("background-color".to_lower_camel_case(), "backgroundColor");
assert_eq!("font-size".to_lower_camel_case(), "fontSize");
// CSS property names commonly use kebab-case
assert_eq!("border-top-width".to_lower_camel_case(), "borderTopWidth");
assert_eq!("margin-left".to_lower_camel_case(), "marginLeft");
}ToLowerCamelCase handles both snake_case and kebab-case inputs.
The Trait Definition
use heck::ToLowerCamelCase;
// The trait is defined approximately as:
// pub trait ToLowerCamelCase {
// fn to_lower_camel_case(&self) -> String;
// }
//
// It's implemented for:
// - str
// - String
// - &str (via str implementation)
fn trait_usage() {
// Works on &str
let result1 = "hello_world".to_lower_camel_case();
// Works on String
let owned = String::from("my_variable");
let result2 = owned.to_lower_camel_case();
// Works on &String
let reference = &owned;
let result3 = reference.to_lower_camel_case();
}The trait is implemented for string types, enabling method calls on string slices and owned strings.
JavaScript Interop
use heck::ToLowerCamelCase;
fn javascript_interop() {
// Rust uses snake_case for functions and variables
// JavaScript uses camelCase conventionally
let rust_function_names = vec![
"get_user_by_id",
"create_new_session",
"delete_all_records",
"parse_json_response",
];
let js_function_names: Vec<String> = rust_function_names
.iter()
.map(|s| s.to_lower_camel_case())
.collect();
assert_eq!(js_function_names, vec![
"getUserById",
"createNewSession",
"deleteAllRecords",
"parseJsonResponse",
]);
}A common use case is generating JavaScript-compatible names from Rust conventions.
JSON Field Name Transformation
use heck::ToLowerCamelCase;
use serde::{Serialize, Deserialize};
// When serializing Rust structs to JSON for JavaScript APIs,
// field names need to follow camelCase convention
#[derive(Serialize)]
struct User {
// Rust uses snake_case fields
user_id: u32,
first_name: String,
last_name: String,
created_at: String,
is_admin: bool,
}
fn json_field_names() {
// Without transformation, JSON would have snake_case keys
// With serde rename, or with manual conversion:
let field_names = vec!["user_id", "first_name", "last_name", "created_at", "is_admin"];
let json_keys: Vec<String> = field_names
.into_iter()
.map(|s| s.to_lower_camel_case())
.collect();
assert_eq!(json_keys, vec![
"userId",
"firstName",
"lastName",
"createdAt",
"isAdmin",
]);
}Converting Rust field names to JSON camelCase keys for API compatibility.
CSS Class Name Generation
use heck::ToLowerCamelCase;
fn css_class_names() {
// CSS commonly uses kebab-case, but JavaScript DOM APIs use camelCase
let css_properties = vec![
"background-color",
"font-size",
"margin-left",
"border-top-width",
];
let js_properties: Vec<String> = css_properties
.iter()
.map(|s| s.to_lower_camel_case())
.collect();
assert_eq!(js_properties, vec![
"backgroundColor",
"fontSize",
"marginLeft",
"borderTopWidth",
]);
}CSS kebab-case to JavaScript DOM property conversion.
Database Column Mapping
use heck::ToLowerCamelCase;
fn database_mapping() {
// Databases often use snake_case columns
// ORMs may need to convert to camelCase for structs
let columns = vec![
"user_id",
"email_address",
"created_at",
"updated_at",
"is_active",
];
let struct_fields: Vec<String> = columns
.iter()
.map(|s| s.to_lower_camel_case())
.collect();
assert_eq!(struct_fields, vec![
"userId",
"emailAddress",
"createdAt",
"updatedAt",
"isActive",
]);
}Converting database column names to struct field names.
Code Generation Use Case
use heck::ToLowerCamelCase;
// Code generation is a primary use case for heck
// Generate JavaScript/TypeScript from Rust definitions
fn generate_js_function(rust_name: &str, params: &[&str]) -> String {
let js_name = rust_name.to_lower_camel_case();
let js_params: Vec<String> = params
.iter()
.map(|p| p.to_lower_camel_case())
.collect();
format!(
"function {}({}) {{\n // implementation\n}}",
js_name,
js_params.join(", ")
)
}
fn code_generation() {
let js_code = generate_js_function(
"create_user_account",
&["user_name", "email_address", "is_active"]
);
assert_eq!(js_code,
"function createUserAccount(userName, emailAddress, isActive) {\n // implementation\n}"
);
}Code generators use ToLowerCamelCase to produce idiomatic target language identifiers.
Mixed Word Boundaries
use heck::ToLowerCamelCase;
fn word_boundaries() {
// heck recognizes several word boundary patterns:
// Underscores
assert_eq!("hello_world".to_lower_camel_case(), "helloWorld");
// Dashes
assert_eq!("hello-world".to_lower_camel_case(), "helloWorld");
// Mixed underscores and dashes
assert_eq!("hello_world-test".to_lower_camel_case(), "helloWorldTest");
// Case transitions are NOT split (unlike some other converters)
// "helloWorld" stays as is, not split into "hello" "world"
assert_eq!("helloWorld".to_lower_camel_case(), "helloWorld");
// Numbers are treated as separate words
assert_eq!("version_2_update".to_lower_camel_case(), "version2Update");
}heck splits on underscores and dashes, but doesn't split on case transitions.
Comparison with UpperCamelCase
use heck::{ToLowerCamelCase, ToUpperCamelCase};
fn camel_case_comparison() {
let input = "my_variable_name";
// lowerCamelCase - first word lowercase
let lower = input.to_lower_camel_case();
assert_eq!(lower, "myVariableName");
// UpperCamelCase (PascalCase) - first word capitalized
let upper = input.to_upper_camel_case();
assert_eq!(upper, "MyVariableName");
// Use lowerCamelCase for:
// - JavaScript functions and variables
// - JSON object keys (by convention)
// - Java methods
// - C# methods and properties
// Use UpperCamelCase for:
// - JavaScript classes
// - TypeScript interfaces
// - Java classes
// - C# classes
}heck provides both ToLowerCamelCase and ToUpperCamelCase traits.
Integration with Serde
use heck::ToLowerCamelCase;
use serde::Serialize;
// For complex renaming, combine heck with serde
#[derive(Serialize)]
struct ApiResponse {
// Serde provides #[serde(rename = "...")] for simple cases
// But for programmatic renaming, use heck in build scripts
user_id: u32,
first_name: String,
}
fn serde_integration() {
// Build.rs can use heck to generate serde attributes
let field_names = vec!["user_id", "first_name", "last_name"];
for field in field_names {
let camel = field.to_lower_camel_case();
println!("#[serde(rename = \"{}\")]", camel);
}
// Generates:
// #[serde(rename = "userId")]
// #[serde(rename = "firstName")]
// #[serde(rename = "lastName")]
}heck can be used in build scripts to generate serde rename attributes.
Iteration Over Words
use heck::ToLowerCamelCase;
fn how_it_works() {
// Internally, heck:
// 1. Splits on underscores and dashes
// 2. Lowercases all words
// 3. Capitalizes all words except the first
// 4. Joins them together
let input = "MY_VARIABLE_NAME";
let result = input.to_lower_camel_case();
// Words: ["MY", "VARIABLE", "NAME"]
// Lowercased: ["my", "variable", "name"]
// Capitalized (except first): ["my", "Variable", "Name"]
// Joined: "myVariableName"
assert_eq!(result, "myVariableName");
}The transformation lowercases input, capitalizes subsequent words, and joins them.
Handling Acronyms
use heck::ToLowerCamelCase;
fn acronyms() {
// heck doesn't specially handle acronyms
// It treats them as regular words
assert_eq!("api_response".to_lower_camel_case(), "apiResponse");
assert_eq!("html_parser".to_lower_camel_case(), "htmlParser");
assert_eq!("http_request".to_lower_camel_case(), "httpRequest");
assert_eq!("user_id".to_lower_camel_case(), "userId");
// If you need special acronym handling (like "parseHTML", "sendHTTP"),
// you'd need custom logic beyond what heck provides
}heck treats acronyms as regular words without special casing rules.
Empty and Edge Cases
use heck::ToLowerCamelCase;
fn edge_cases() {
// Empty string
assert_eq!("".to_lower_camel_case(), "");
// Single character
assert_eq!("a".to_lower_camel_case(), "a");
// Only separators
assert_eq!("_".to_lower_camel_case(), "");
assert_eq!("__".to_lower_camel_case(), "");
assert_eq!("-_-".to_lower_camel_case(), "");
// Numbers
assert_eq!("version_2".to_lower_camel_case(), "version2");
assert_eq!("2_fast_2_furious".to_lower_camel_case(), "2Fast2Furious");
// Already camelCase (no underscores/dashes)
assert_eq!("alreadyCamelCase".to_lower_camel_case(), "alreadyCamelCase");
}Edge cases are handled gracefully: separators are removed, empty segments are skipped.
Performance Considerations
use heck::ToLowerCamelCase;
fn performance() {
// ToLowerCamelCase allocates a new String
// The input is not modified
let input = "my_variable_name";
let output = input.to_lower_camel_case();
// Input is still valid
assert_eq!(input, "my_variable_name");
// Output is a new String
assert_eq!(output, "myVariableName");
// For repeated conversions, consider caching:
// - Use lazy_static or once_cell for static values
// - Use a HashMap for dynamic conversions
}to_lower_camel_case() allocates a new String each time.
Related Traits in heck
use heck::{ToLowerCamelCase, ToUpperCamelCase, ToSnakeCase, ToKebabCase};
fn related_traits() {
let input = "myVariableName";
// heck provides many case conversion traits:
// ToLowerCamelCase: "myVariableName" -> "myVariableName" (for snake_case input)
assert_eq!("my_variable_name".to_lower_camel_case(), "myVariableName");
// ToUpperCamelCase: "my_variable_name" -> "MyVariableName"
assert_eq!("my_variable_name".to_upper_camel_case(), "MyVariableName");
// ToSnakeCase: "myVariableName" -> "my_variable_name" (if split on case)
// Note: heck's snake_case doesn't split on case transitions
assert_eq!("my_variable_name".to_snake_case(), "my_variable_name");
// ToKebabCase: "my_variable_name" -> "my-variable-name"
assert_eq!("my_variable_name".to_kebab_case(), "my-variable-name");
// ToShoutySnakeCase: "my_variable_name" -> "MY_VARIABLE_NAME"
assert_eq!("my_variable_name".to_shouty_snake_case(), "MY_VARIABLE_NAME");
// ToTitleCase: "my_variable_name" -> "My Variable Name"
assert_eq!("my_variable_name".to_title_case(), "My Variable Name");
// ToPascalCase: alias for ToUpperCamelCase
assert_eq!("my_variable_name".to_pascal_case(), "MyVariableName");
}heck provides many case conversion traits for different naming conventions.
Practical Example: API Types Generator
use heck::ToLowerCamelCase;
struct FieldDefinition {
rust_name: String,
rust_type: String,
}
struct ApiTypeGenerator {
name: String,
fields: Vec<FieldDefinition>,
}
impl ApiTypeGenerator {
fn generate_typescript(&self) -> String {
let interface_name = self.name.to_upper_camel_case();
let fields: Vec<String> = self.fields
.iter()
.map(|f| {
let ts_name = f.rust_name.to_lower_camel_case();
let ts_type = rust_type_to_typescript(&f.rust_type);
format!(" {}: {};", ts_name, ts_type)
})
.collect();
format!(
"interface {} {{\n{}\n}}",
interface_name,
fields.join("\n")
)
}
}
fn rust_type_to_typescript(rust_type: &str) -> &'static str {
match rust_type {
"String" => "string",
"u32" | "i32" | "u64" | "i64" => "number",
"bool" => "boolean",
_ => "any",
}
}
fn type_generator_example() {
use heck::ToUpperCamelCase;
let generator = ApiTypeGenerator {
name: "user_account".to_string(),
fields: vec![
FieldDefinition { rust_name: "user_id".into(), rust_type: "u32".into() },
FieldDefinition { rust_name: "email_address".into(), rust_type: "String".into() },
FieldDefinition { rust_name: "is_active".into(), rust_type: "bool".into() },
],
};
let ts = generator.generate_typescript();
assert_eq!(ts,
"interface UserAccount {\n userId: number;\n emailAddress: string;\n isActive: boolean;\n}"
);
}Synthesis
Quick reference:
use heck::ToLowerCamelCase;
fn quick_reference() {
// Basic usage
let camel = "my_variable_name".to_lower_camel_case();
assert_eq!(camel, "myVariableName");
// Also handles kebab-case
let from_kebab = "background-color".to_lower_camel_case();
assert_eq!(from_kebab, "backgroundColor");
// First word lowercase, subsequent words capitalized
// snake_case -> lowerCamelCase
// kebab-case -> lowerCamelCase
// Use when converting Rust identifiers (snake_case) to:
// - JavaScript function/variable names
// - JSON object keys (by convention)
// - Java method names
// - CSS DOM property names
}Key insight: heck::ToLowerCamelCase serves a specific need in the Rust ecosystem: converting identifiers between naming conventions. Rust conventionally uses snake_case for function names, variables, and fields, while many other contextsâJavaScript APIs, JSON serialization conventions, Java interop, CSS DOM propertiesâexpect camelCase identifiers. The trait provides a simple, consistent way to perform this transformation without manually handling word boundaries, capitalization, and edge cases. Unlike regex-based solutions that might incorrectly split on case transitions or mishandle acronyms, heck implements a consistent word-splitting strategy based on underscores and dashes. The primary use cases are code generation (building JavaScript/TypeScript from Rust definitions), serialization layer bridges (generating JSON field names), and database ORM mappings (converting column names to struct fields). ToLowerCamelCase is specifically the "lower" variant where the first word remains lowercaseâappropriate for functions, methods, and variablesâas opposed to ToUpperCamelCase (PascalCase) which is appropriate for types and classes.
