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.