Motivation and Rationale
Rust's current conditional syntax provides excellent ergonomics with if let expressions, which have become a beloved feature for their clarity and conciseness. However, there exists an artificial limitation in the syntax that breaks the natural flow of conditional reasoning when developers need to combine boolean conditions with structural pattern matching.
The proposed extension addresses a fundamental inconsistency: while if let enjoys first-class support in both primary and secondary conditional positions, the more powerful match expression is artificially constrained to require block nesting when following an else clause.
User Experience and Readability Benefits
The proposed syntax directly maps to natural thought patterns. When reading code, developers think "if this condition, then this behavior, otherwise try this pattern matching." The current syntax forces unnecessary block nesting that breaks this mental flow.
Current syntax:
if condition {
handle_positive();
} else {
match value {
Pattern1 => handle_pattern1(),
Pattern2 => handle_pattern2(),
}
}
Proposed syntax:
if condition {
handle_positive();
} else match value {
Pattern1 => handle_pattern1(),
Pattern2 => handle_pattern2(),
}
Consistency and Design Philosophy
The proposal maintains consistency with existing conditional syntax patterns while extending them logically. Rust already demonstrates that conditional syntax should serve the developer's logical flow rather than impose arbitrary structural constraints.
The precedent of if let in secondary positions establishes that pattern matching belongs in conditional contexts, and match as the more general form deserves equal syntactic treatment.
fn parse_config(input: &str) -> Result<Config, ParseError> {
if input.trim().is_empty() {
Err(ParseError::EmptyInput)
} else match input.parse::<toml::Value>() {
Ok(toml_value) => {
match toml_value.as_table() {
Some(table) => Config::from_table(table),
None => Err(ParseError::InvalidFormat),
}
}
Err(_) => match input.parse::<json::Value>() {
Ok(json_value) => Config::from_json(&json_value),
Err(_) => Err(ParseError::UnsupportedFormat),
}
}
}
or
if let Some(resource) = acquire_resource() && resource.is_valid() {
use_resource(resource); // resource is moved and available here
} else match get_alternative_data() { // different data source
Alternative::Good(data) => process(data),
Alternative::Bad => fallback(),
}
Addressing the match guard alternatives
While pattern guards can technically achieve similar results, they introduce significant practical drawbacks that make the proposed syntax extension valuable:
// Using pattern guards - problematic for complex conditions
match value.hard_job() { // This executes even if first condition passes!
_ if expensive_validation() && another_check() && yet_another_condition() => handle_positive(),
Pattern1 => handle_pattern1(),
Pattern2 => handle_pattern2(),
}
// Proposed syntax - clear separation of concerns
if expensive_validation() && another_check() && yet_another_condition() {
handle_positive();
} else match value { // Only evaluated when needed
Pattern1 => handle_pattern1(),
Pattern2 => handle_pattern2(),
}
Pattern guards evaluate all patterns sequentially, potentially executing expensive computations unnecessarily. The conditional approach allows early termination and avoids pattern evaluation when the boolean condition already determines the flow.
Cognitive Load
// Guards obscure the primary logic flow
match input {
_ if input.starts_with("HTTP/1.1") && input.contains("200 OK") && !input.contains("Connection: close") => handle_keepalive(),
_ if input.starts_with("HTTP/1.1") && input.contains("200 OK") => handle_response(),
_ if input.starts_with("HTTP/1.1") => handle_http_error(),
_ if input.starts_with("HTTP/2") => handle_http2(),
// ... more complex guard chains
}
// Proposed syntax - clear conditional flow
if input.starts_with("HTTP/1.1") && input.contains("200 OK") && !input.contains("Connection: close") {
handle_keepalive();
} else if input.starts_with("HTTP/1.1") && input.contains("200 OK") {
handle_response();
} else match input {
line if line.starts_with("HTTP/1.1") => handle_http_error(),
line if line.starts_with("HTTP/2") => handle_http2(),
_ => handle_unknown(),
}
Practical Impact
The proposed syntax enables clear separation between boolean logic (which may involve side effects, moves, or expensive computations) and structural pattern matching, which guards cannot provide effectively.
This addresses real-world pain points in codebases of moderate complexity. Applications dealing with user input validation, configuration processing, and data transformation frequently encounter scenarios where boolean preconditions naturally precede structural pattern matching.
This proposal aims to enhance Rust's conditional syntax consistency and developer experience. What do you think about this proposal?