Loading pageā¦
Rust walkthroughs
Loading pageā¦
strum::AsRefStr and strum::IntoStaticStr for string conversion from enums?strum::AsRefStr and strum::IntoStaticStr are derive macros in the strum crate that provide different ownership semantics for converting enum variants to string references. AsRefStr implements AsRef<str>, returning a borrowed reference to the variant's string representation with lifetime tied to the enum instanceāit works with any display representation. IntoStaticStr implements Into<&'static str>, returning a 'static lifetime string reference that can outlive the enum instance, but only works when the string representation is statically known at compile time (such as using the variant name directly). The key trade-off is flexibility versus ownership: AsRefStr supports custom #[strum(to_string = "...")] attributes but requires the enum to remain in scope, while IntoStaticStr provides 'static references but only for fixed variant names.
use strum::AsRefStr;
#[derive(AsRefStr)]
enum Status {
Active,
Inactive,
Pending,
}
fn demonstrate_as_ref() {
let status = Status::Active;
// Returns &str borrowed from the enum instance
let s: &str = status.as_ref();
// s is valid only while status is in scope
println!("Status: {}", s);
// Works with standard AsRef pattern
fn accepts_as_ref(value: impl AsRef<str>) {
println!("Got: {}", value.as_ref());
}
accepts_as_ref(&status);
accepts_as_ref(status);
}AsRefStr returns a borrowed reference tied to the enum's lifetime.
use strum::IntoStaticStr;
#[derive(IntoStaticStr)]
enum Status {
Active,
Inactive,
Pending,
}
fn demonstrate_into_static() {
let status = Status::Active;
// Returns &'static str that lives forever
let s: &'static str = status.into();
// Can use s even after status is dropped
println!("Status: {}", s);
// Useful for returning strings from functions
fn get_status_name(status: Status) -> &'static str {
status.into()
}
let static_str: &'static str = get_status_name(Status::Pending);
// static_str lives forever, no lifetime constraints
}IntoStaticStr returns 'static references independent of the enum instance.
use strum::{AsRefStr, IntoStaticStr};
// AsRefStr supports custom to_string values
#[derive(AsRefStr)]
enum StatusWithCustom {
#[strum(to_string = "status-active")]
Active,
#[strum(to_string = "status-inactive")]
Inactive,
}
fn custom_as_ref() {
let status = StatusWithCustom::Active;
assert_eq!(status.as_ref(), "status-active");
}
// IntoStaticStr ignores custom to_string - uses variant name
#[derive(IntoStaticStr)]
enum StatusWithCustomIgnored {
#[strum(to_string = "status-active")] // Ignored by IntoStaticStr
Active,
#[strum(to_string = "status-inactive")] // Ignored by IntoStaticStr
Inactive,
}
fn custom_into_static() {
let status = StatusWithCustomIgnored::Active;
// Uses variant name, not custom string
assert_eq!(status.into(), "Active");
}AsRefStr respects to_string attributes; IntoStaticStr uses variant names directly.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr)]
enum Color {
Red,
Green,
Blue,
}
#[derive(IntoStaticStr)]
enum Shape {
Circle,
Square,
Triangle,
}
fn lifetime_comparison() {
// AsRefStr: borrowed reference
let color = Color::Red;
let color_ref: &str = color.as_ref();
// color_ref lifetime tied to color
// IntoStaticStr: static reference
let shape = Shape::Circle;
let shape_static: &'static str = shape.into();
// shape_static has 'static lifetime
}
// IntoStaticStr enables returning static strings
fn get_shape_name(shape: Shape) -> &'static str {
shape.into() // Works because IntoStaticStr returns 'static
}
// AsRefStr requires the enum to live long enough
fn get_color_name(color: &Color) -> &str {
color.as_ref() // Lifetime tied to color reference
}
// This would NOT compile with AsRefStr:
// fn get_color_name_bad(color: Color) -> &'static str {
// color.as_ref() // ERROR: cannot return reference to dropped value
// }
// This compiles with IntoStaticStr:
fn get_shape_name_owned(shape: Shape) -> &'static str {
shape.into() // OK: returns 'static str
}IntoStaticStr enables returning &'static str from functions; AsRefStr cannot.
use strum::{AsRefStr, IntoStaticStr, Display};
// All three together
#[derive(AsRefStr, IntoStaticStr, Display)]
enum Direction {
North,
South,
East,
West,
}
fn comparing_traits() {
let dir = Direction::North;
// AsRefStr: AsRef<str> implementation
let as_ref: &str = dir.as_ref();
assert_eq!(as_ref, "North");
// IntoStaticStr: Into<&'static str> implementation
let into_static: &'static str = dir.into();
assert_eq!(into_static, "North");
// Display: std::fmt::Display implementation
let display = format!("{}", dir);
assert_eq!(display, "North");
// Key differences:
// - as_ref() requires dir to remain in scope
// - into() consumes dir, returns 'static str
// - format!() allocates a new String
}Three different trait implementations with different ownership semantics.
use strum::{AsRefStr, IntoStaticStr};
use std::collections::HashMap;
#[derive(AsRefStr, IntoStaticStr, Clone, Copy)]
enum HttpMethod {
Get,
Post,
Put,
Delete,
}
fn collections_comparison() {
// AsRefStr: Works with references
let methods = [HttpMethod::Get, HttpMethod::Post];
let names: Vec<&str> = methods.iter().map(|m| m.as_ref()).collect();
// names borrows from methods
// IntoStaticStr: Owns the strings (static lifetime)
let methods2 = [HttpMethod::Get, HttpMethod::Post, HttpMethod::Put];
let names2: Vec<&'static str> = methods2.iter().map(|m| (*m).into()).collect();
// names2 has 'static lifetime, methods2 can be dropped
// Useful for map keys
let mut routes: HashMap<&'static str, fn()> = HashMap::new();
routes.insert(HttpMethod::Get.into(), handle_get);
routes.insert(HttpMethod::Post.into(), handle_post);
}
fn handle_get() {}
fn handle_post() {}IntoStaticStr is useful when you need collection elements to outlive the source.
use strum::IntoStaticStr;
#[derive(IntoStaticStr)]
enum Message {
Start,
Stop,
Pause,
Resume,
}
fn match_static_string(msg: Message) {
// Convert to static string for matching
let s: &'static str = msg.into();
match s {
"Start" => println!("Starting..."),
"Stop" => println!("Stopping..."),
"Pause" => println!("Pausing..."),
"Resume" => println!("Resuming..."),
_ => unreachable!(),
}
}
// Compare with enum matching (preferred approach)
fn match_enum(msg: Message) {
match msg {
Message::Start => println!("Starting..."),
Message::Stop => println!("Stopping..."),
Message::Pause => println!("Pausing..."),
Message::Resume => println!("Resuming..."),
}
}Static strings from IntoStaticStr enable string-based pattern matching.
use strum::{AsRefStr, IntoStaticStr};
use serde::Serialize;
// AsRefStr for serialization with borrowed strings
#[derive(AsRefStr, Serialize)]
#[serde(into = "&str")]
enum StatusAsRef {
Active,
Inactive,
}
// IntoStaticStr for serialization with static strings
#[derive(IntoStaticStr, Serialize)]
#[serde(into = "&'static str")]
enum StatusStatic {
Active,
Inactive,
}
fn serialization() {
// Both work for serialization
let status = StatusAsRef::Active;
let json = serde_json::to_string(&status).unwrap();
assert_eq!(json, r#""Active""#);
}IntoStaticStr can eliminate lifetime complexity in serialization scenarios.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr, IntoStaticStr)]
enum Size {
Small,
Medium,
Large,
}
fn to_string_comparison() {
let size = Size::Medium;
// AsRefStr: AsRef -> &str, then to_string allocates
let s1: String = size.as_ref().to_string();
// IntoStaticStr: into() gives &'static str, then to_string allocates
let s2: String = size.into();
let s2: String = s2.to_string();
// Both result in owned String, but different paths:
// AsRefStr: enum -> &str -> String (borrowed intermediate)
// IntoStaticStr: enum -> &'static str -> String (static intermediate)
}Both can produce owned String, but the intermediate references differ.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr, IntoStaticStr)]
enum Priority {
Low,
Medium,
High,
}
// Function accepting AsRefStr types
fn process_as_ref<T: AsRef<str>>(value: T) {
println!("Processing: {}", value.as_ref());
}
// Function accepting IntoStaticStr types
fn process_static<T: Into<&'static str>>(value: T) {
let s: &'static str = value.into();
println!("Processing: {}", s);
}
fn using_generic_bounds() {
let priority = Priority::High;
// AsRefStr works with AsRef bounds
process_as_ref(&priority);
// IntoStaticStr works with Into<&'static str> bounds
process_static(priority);
}Different trait bounds for different use cases.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr, IntoStaticStr, Clone, Copy)]
enum Value {
A,
B,
C,
}
fn performance() {
// AsRefStr: Reference to variant's string representation
// - No allocation
// - Reference tied to enum lifetime
// - Slight overhead for dynamic dispatch through trait
// IntoStaticStr: Static string literal
// - No allocation
// - 'static lifetime
// - Direct pointer to static data
// Both are zero-cost at runtime for the conversion itself
// The difference is lifetime management
}Both conversions are zero-allocation; the difference is lifetime semantics.
use strum::{AsRefStr, IntoStaticStr, Display, EnumString};
// Derive multiple string traits together
#[derive(AsRefStr, IntoStaticStr, Display, EnumString, Clone, Copy)]
enum Protocol {
#[strum(to_string = "http", serialize = "http")]
Http,
#[strum(to_string = "https", serialize = "https")]
Https,
#[strum(to_string = "ws", serialize = "ws")]
WebSocket,
#[strum(to_string = "wss", serialize = "wss")]
WebSocketSecure,
}
fn combined_usage() {
let proto = Protocol::Https;
// Different use cases:
// AsRefStr - for borrowing when enum lives long enough
let borrowed: &str = proto.as_ref();
assert_eq!(borrowed, "https");
// IntoStaticStr - for 'static references
let static_ref: &'static str = proto.into();
// Note: IntoStaticStr ignores to_string, uses variant name
assert_eq!(static_ref, "Https"); // Variant name!
// Display - for user-facing output
let display: String = format!("{}", proto);
assert_eq!(display, "https"); // Uses to_string
// EnumString - for parsing strings back to enum
let parsed: Protocol = "https".parse().unwrap();
assert_eq!(parsed, Protocol::Https);
}Combining traits provides flexibility for different scenarios.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr, IntoStaticStr)]
enum HeaderName {
ContentType,
ContentLength,
Authorization,
CacheControl,
}
impl HeaderName {
// Using IntoStaticStr for 'static header names
fn as_static_str(self) -> &'static str {
self.into()
}
}
fn http_headers() {
let header = HeaderName::ContentType;
// Header name as 'static str - useful for header maps
let name: &'static str = header.into();
// Can use in static contexts
static DEFAULT_HEADERS: &[&'static str] = &[
"ContentType", // Can't use HeaderName::ContentType.into() here
"ContentLength",
];
// At runtime, convert for use
let headers: Vec<&'static str> = vec![
HeaderName::ContentType.into(),
HeaderName::Authorization.into(),
];
}IntoStaticStr provides 'static references useful for static contexts.
use strum::{AsRefStr, IntoStaticStr};
#[derive(AsRefStr, IntoStaticStr, Clone, Copy)]
enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
fn log_level_example() {
let level = LogLevel::Error;
// AsRefStr: Good for temporary references
fn log_message(level: &LogLevel, msg: &str) {
println!("[{}] {}", level.as_ref(), msg);
}
log_message(&level, "Something went wrong");
// IntoStaticStr: Good for static storage
static LEVEL_NAMES: &[&'static str] = &["Trace", "Debug", "Info", "Warn", "Error"];
// Convert to static for lookups
let level_name: &'static str = level.into();
assert!(LEVEL_NAMES.contains(&level_name));
}Different traits for different lifetime requirements.
Ownership and Lifetime Comparison:
| Aspect | AsRefStr | IntoStaticStr |
|--------|------------|-----------------|
| Returns | &str (borrowed) | &'static str |
| Lifetime | Tied to enum instance | 'static |
| Custom strings | Supported via to_string | Ignored (variant name only) |
| Method | .as_ref() | .into() |
| Trait | AsRef<str> | Into<&'static str> |
| Consumes | No | Yes |
When to use each:
| Scenario | Choice | Reason |
|----------|--------|--------|
| Temporary reference | AsRefStr | Borrowed lifetime sufficient |
| Return from function | IntoStaticStr | 'static lifetime needed |
| Custom string values | AsRefStr | Supports to_string attribute |
| Static collections | IntoStaticStr | 'static required for storage |
| Serialization | Both work | Choose based on ownership needs |
| Generic bounds | AsRefStr | More flexible AsRef<str> bound |
Key insight: AsRefStr and IntoStaticStr serve fundamentally different ownership models. AsRefStr implements AsRef<str>, borrowing from the enum instanceāthis supports custom string representations via to_string but requires the enum to remain in scope. IntoStaticStr implements Into<&'static str>, returning compile-time string literals from the variant names themselvesāthis provides 'static references that can outlive the enum but ignores any custom to_string attributes. Choose AsRefStr when you need custom strings or when borrowing is acceptable. Choose IntoStaticStr when you need 'static references for storage, return values, or static contexts, and when variant names are acceptable as the string representation.