What is the purpose of heck::ToKebabCase for converting identifiers to kebab-case strings?
heck::ToKebabCase is a trait that converts identifiers to kebab-case strings (lowercase with hyphens) by splitting on word boundaries and joining with hyphens, commonly used for CLI flags, configuration keys, and URL slugs. It provides automatic case conversion from various input formats like CamelCase, snake_case, and SHOUTY_CASE to consistent kebab-case output.
Basic ToKebabCase Usage
use heck::ToKebabCase;
fn basic_usage() {
// ToKebabCase converts any case to kebab-case
// From CamelCase
assert_eq!("MyStruct".to_kebab_case(), "my-struct");
assert_eq!("HTTPServer".to_kebab_case(), "http-server");
// From snake_case
assert_eq!("my_function".to_kebab_case(), "my-function");
assert_eq!("user_id".to_kebab_case(), "user-id");
// From SHOUTY_CASE
assert_eq!("MAX_CONNECTIONS".to_kebab_case(), "max-connections");
assert_eq!("API_KEY".to_kebab_case(), "api-key");
// From mixed case
assert_eq!("myHTTPServer".to_kebab_case(), "my-http-server");
assert_eq!("XMLHttpRequest".to_kebab_case(), "xml-http-request");
}ToKebabCase converts identifiers from any case convention to kebab-case.
The ToKebabCase Trait
// heck provides the ToKebabCase trait
// It's implemented for &str and String
pub trait ToKebabCase {
fn to_kebab_case(&self) -> String;
}
// The trait is in heck's prelude
use heck::ToKebabCase;
fn trait_signature() {
// The method takes &self and returns String
// Input: any string
// Output: kebab-case string
let input = "MyFunctionName";
let output: String = input.to_kebab_case();
assert_eq!(output, "my-function-name");
}The trait is simple: to_kebab_case(&self) -> String.
Word Boundary Detection
use heck::ToKebabCase;
fn word_boundaries() {
// heck detects word boundaries using several rules:
// 1. Uppercase letters start new words
assert_eq!("CamelCase".to_kebab_case(), "camel-case");
assert_eq!("PascalCase".to_kebab_case(), "pascal-case");
// 2. Underscores are word separators
assert_eq!("snake_case".to_kebab_case(), "snake-case");
assert_eq!("my_variable_name".to_kebab_case(), "my-variable-name");
// 3. Hyphens are preserved/treated as separators
assert_eq!("kebab-case".to_kebab_case(), "kebab-case");
// 4. Spaces are word separators
assert_eq!("spaced words".to_kebab_case(), "spaced-words");
// 5. Numbers are word boundaries
assert_eq!("version2Update".to_kebab_case(), "version-2-update");
assert_eq!("IPv4Address".to_kebab_case(), "ipv-4-address");
}ToKebabCase detects word boundaries from uppercase, underscores, hyphens, spaces, and numbers.
Acronym and Abbreviation Handling
use heck::ToKebabCase;
fn acronym_handling() {
// Acronyms are detected and converted appropriately
// Multiple uppercase letters are grouped
assert_eq!("XMLParser".to_kebab_case(), "xml-parser");
assert_eq!("HTTPServer".to_kebab_case(), "http-server");
assert_eq!("APIEndpoint".to_kebab_case(), "api-endpoint");
// Transitions from uppercase to lowercase split
assert_eq!("HTTPRequest".to_kebab_case(), "http-request");
// SHOUTY_CASE converts properly
assert_eq!("API_KEY".to_kebab_case(), "api-key");
assert_eq!("HTTP_RESPONSE_CODE".to_kebab_case(), "http-response-code");
// Mixed acronyms
assert_eq!("XMLHTTPRequest".to_kebab_case(), "xml-http-request");
}Acronyms are grouped and converted to lowercase with proper hyphenation.
CLI Flag Generation
use heck::ToKebabCase;
fn cli_flags() {
// Common use: generating CLI flag names from field names
struct Config {
max_connections: u32,
retry_timeout: u32,
enable_logging: bool,
output_file: String,
}
// Convert struct field names to CLI flags
fn field_to_flag(field: &str) -> String {
format!("--{}", field.to_kebab_case())
}
assert_eq!(field_to_flag("max_connections"), "--max-connections");
assert_eq!(field_to_flag("retry_timeout"), "--retry-timeout");
assert_eq!(field_to_flag("enable_logging"), "--enable-logging");
assert_eq!(field_to_flag("output_file"), "--output-file");
}CLI applications commonly use ToKebabCase for converting Rust field names to flag names.
Configuration Key Normalization
use heck::ToKebabCase;
use std::collections::HashMap;
fn config_normalization() {
// Normalize configuration keys to kebab-case
// Input config might use various conventions
let raw_config = HashMap::from([
("database_url", "postgres://localhost"),
("maxConnections", "100"),
("API_KEY", "secret123"),
("retry_timeout_seconds", "30"),
]);
// Normalize all keys to kebab-case
let normalized: HashMap<String, &str> = raw_config
.into_iter()
.map(|(k, v)| (k.to_kebab_case(), v))
.collect();
assert_eq!(normalized.get("database-url"), Some(&"postgres://localhost"));
assert_eq!(normalized.get("max-connections"), Some(&"100"));
assert_eq!(normalized.get("api-key"), Some(&"secret123"));
assert_eq!(normalized.get("retry-timeout-seconds"), Some(&"30"));
}Configuration systems can normalize keys from various sources to consistent kebab-case.
URL Slug Generation
use heck::ToKebabCase;
fn url_slugs() {
// Generate URL-friendly slugs from titles
fn generate_slug(title: &str) -> String {
title.to_kebab_case()
}
// Blog titles to slugs
assert_eq!(
generate_slug("How to Build a REST API"),
"how-to-build-a-rest-api"
);
assert_eq!(
generate_slug("Introduction to Rust Programming"),
"introduction-to-rust-programming"
);
// Product names to slugs
assert_eq!(
generate_slug("Product Name With Special Characters"),
"product-name-with-special-characters"
);
}URL slug generation uses kebab-case for readability and URL safety.
Integration with clap
use heck::ToKebabCase;
// Common pattern: derive CLI from struct fields
fn clap_integration() {
// When building CLI apps, convert field names to kebab-case flags
struct Args {
input_file: String,
output_directory: String,
max_retries: u32,
verbose_mode: bool,
}
// Generate flag names
let flags = [
"input_file".to_kebab_case(),
"output_directory".to_kebab_case(),
"max_retries".to_kebab_case(),
"verbose_mode".to_kebab_case(),
];
assert_eq!(flags, [
"input-file",
"output-directory",
"max-retries",
"verbose-mode"
]);
// These become CLI flags:
// --input-file, --output-directory, --max-retries, --verbose-mode
}CLI frameworks like clap often use kebab-case for argument names.
Comparison with Other heck Traits
use heck::{ToKebabCase, ToSnakeCase, ToCamelCase, ToShoutySnakeCase, ToTitleCase, ToLowerCamelCase};
fn case_comparison() {
let input = "my_function_name";
// Each trait converts to a different case
assert_eq!(input.to_kebab_case(), "my-function-name"); // kebab-case
assert_eq!(input.to_snake_case(), "my_function_name"); // snake_case
assert_eq!(input.to_camel_case(), "MyFunctionName"); // CamelCase
assert_eq!(input.to_lower_camel_case(), "myFunctionName"); // camelCase
assert_eq!(input.to_shouty_snake_case(), "MY_FUNCTION_NAME"); // SHOUTY_CASE
assert_eq!(input.to_title_case(), "My Function Name"); // Title Case
// From a different input
let camel = "MyFunctionName";
assert_eq!(camel.to_kebab_case(), "my-function-name");
assert_eq!(camel.to_snake_case(), "my_function_name");
assert_eq!(camel.to_lower_camel_case(), "myFunctionName");
}heck provides multiple case conversion traits; ToKebabCase is one of them.
Comparison Summary
use heck::ToKebabCase;
fn case_comparison_table() {
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Input β to_kebab_case() β Use Case β
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β "CamelCase" β "camel-case" β CLI flags β
// β "snake_case" β "snake-case" β Config keys β
// β "SHOUTY_CASE" β "shouty-case" β Environment vars β
// β "MixedCASE" β "mixed-case" β Normalization β
// β "HTTPServer" β "http-server" β Acronym handling β
// β "user_id" β "user-id" β API parameter names β
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
}All case styles normalize to kebab-case for consistent usage.
Edge Cases
use heck::ToKebabCase;
fn edge_cases() {
// Empty string
assert_eq!("".to_kebab_case(), "");
// Single word
assert_eq!("word".to_kebab_case(), "word");
assert_eq!("Word".to_kebab_case(), "word");
assert_eq!("WORD".to_kebab_case(), "word");
// Already kebab-case
assert_eq!("already-kebab".to_kebab_case(), "already-kebab");
// Multiple separators
assert_eq!("multi___underscore".to_kebab_case(), "multi-underscore");
assert_eq!("multiple---hyphens".to_kebab_case(), "multiple-hyphens");
// Numbers
assert_eq!("v2.0.0".to_kebab_case(), "v-2-0-0");
assert_eq!("test123".to_kebab_case(), "test-123");
// Special characters (removed or converted)
assert_eq!("hello@world".to_kebab_case(), "hello-world");
// Leading/trailing separators
assert_eq!("_private".to_kebab_case(), "private");
assert_eq!("public_".to_kebab_case(), "public");
}ToKebabCase handles edge cases gracefully, producing consistent output.
Environment Variable Mapping
use heck::ToKebabCase;
fn env_var_mapping() {
// Convert environment variable names to config keys
let env_vars = [
"DATABASE_URL",
"MAX_CONNECTIONS",
"API_KEY",
"LOG_LEVEL",
];
let config_keys: Vec<String> = env_vars
.iter()
.map(|v| v.to_kebab_case())
.collect();
assert_eq!(config_keys, [
"database-url",
"max-connections",
"api-key",
"log-level"
]);
// This enables consistent config regardless of source:
// - Environment variables (SHOUTY_CASE)
// - Config files (kebab-case or snake_case)
// - CLI flags (kebab-case)
}Environment variables in SHOUTY_CASE can be converted to kebab-case config keys.
Serde Integration
use heck::ToKebabCase;
use serde::{Deserialize, Serialize};
fn serde_integration() {
// Common pattern: Rust struct with snake_case fields, serialized to kebab-case
#[derive(Serialize, Deserialize)]
struct Config {
// Rust uses snake_case by convention
#[serde(rename = "database-url")]
database_url: String,
#[serde(rename = "max-connections")]
max_connections: u32,
// Manual rename is tedious for many fields
}
// Alternative: generate renames with heck
fn generate_rename(field: &str) -> String {
format!("#[serde(rename = \"{}\")]", field.to_kebab_case())
}
// For field "database_url":
// #[serde(rename = "database-url")]
}ToKebabCase is useful for generating serde rename attributes.
Build Script Usage
// In a build.rs file, generate code with kebab-case names
use heck::ToKebabCase;
fn build_script_example() {
// Generate CLI argument parsing code from struct definition
let fields = [
("input_file", "String", "Input file path"),
("output_dir", "String", "Output directory"),
("max_retries", "u32", "Maximum retry attempts"),
("verbose", "bool", "Enable verbose output"),
];
for (field, _ty, _help) in fields {
let kebab = field.to_kebab_case();
println!("Field '{}' becomes flag '--{}'", field, kebab);
}
// Output:
// Field 'input_file' becomes flag '--input-file'
// Field 'output_dir' becomes flag '--output-dir'
// Field 'max_retries' becomes flag '--max-retries'
// Field 'verbose' becomes flag '--verbose'
}Build scripts can use ToKebabCase to generate code with consistent naming.
Complete Summary
use heck::ToKebabCase;
fn complete_summary() {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Method β to_kebab_case() β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β Input β Any string (CamelCase, snake_case, etc.) β
// β Output β kebab-case (lowercase with hyphens) β
// β Word separators β Uppercase, underscore, hyphen, space, numbers β
// β Acronym handling β Groups uppercase letters together β
// β Performance β Allocates new String β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// β Use Case β Example β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
// β CLI flags β max_connections β --max-connections β
// β Config keys β DatabaseURL β database-url β
// β URL slugs β My Blog Title β my-blog-title β
// β Environment vars β API_KEY β api-key β
// β Serde renames β field_name β "field-name" β
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Key behaviors:
// 1. Detects word boundaries from multiple conventions
// 2. Groups acronyms (HTTPServer β http-server)
// 3. Lowercases everything
// 4. Joins with hyphens
// 5. Handles edge cases (empty, single word, existing kebab)
}
// Key insight:
// heck::ToKebabCase provides automatic conversion from any case convention
// to kebab-case (lowercase with hyphens). It's commonly used for:
//
// - CLI argument names (max_connections β --max-connections)
// - Configuration file keys (consistent format across sources)
// - URL slugs (readable, URL-safe identifiers)
// - Normalizing environment variables (API_KEY β api-key)
//
// The trait handles various input conventions:
// - CamelCase / PascalCase
// - snake_case
// - SHOUTY_CASE
// - Mixed case
// - Already kebab-case (idempotent)
//
// Word boundary detection uses:
// - Uppercase letters (start of new word)
// - Underscores (separator)
// - Hyphens (separator)
// - Spaces (separator)
// - Numbers (boundary)
//
// Acronyms are handled specially:
// - HTTPServer β http-server (not h-t-t-p-server)
// - XMLParser β xml-parser
// - IPv4Address β ipv-4-address
//
// Use ToKebabCase when you need consistent kebab-case output
// from varying input conventions.Key insight: heck::ToKebabCase converts identifiers from any case convention to kebab-case by detecting word boundaries and joining with hyphens. It handles acronyms intelligently (grouping uppercase letters), works with multiple input formats, and produces consistent output suitable for CLI flags, configuration keys, and URL slugs. The trait is essential for normalizing names across systems that use different conventionsβRust's snake_case fields to kebab-case CLI flags, environment variables' SHOUTY_CASE to kebab-case config keys, or arbitrary strings to URL-safe slugs.
