Overview
The design of the Rust language places a strong emphasis on developer experience, which is why it includes many practical “syntax sugars”. These “syntax sugars” make the code more concise and readable while maintaining the language’s power and flexibility.
1. String Interpolation
String interpolation allows us to embed the values of variables or expressions within a string, using {} as placeholders.
fn main() { let name = "World"; let text = format!("Hello {}", name); println!("{}", text);}
2. if-let Expression
if let is a combination of if and let, used for pattern matching and destructuring assignment within an if condition.
fn main() { let x = Some(66); if let Some(value) = x { println!("value is: {}", value); } else { println!("value is None"); }}
3. Range Expressions
.. is the range operator that excludes the end value, while ..= includes the end value.
fn main() { for i in 1..=5 { println!("{}", i); } for i in 1..5 { println!("{}", i); }}
4. Closures
A closure is an anonymous function that can capture variables from its environment. In the example code below, a closure is used to filter even numbers.
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let even_numbers = numbers.iter().filter(|&x| x % 2 == 0).collect::<vec>(); println!("{:?}", even_numbers);}</vec
5. Derive Macros
Using the #[derive(…)] attribute, Rust can automatically generate standard method implementations for structs or enums, such as: Debug, Eq, PartialEq, etc. In the example code below, we use the #[derive(Debug)] attribute for the Person struct type, so the compiler automatically generates the implementation of the Debug Trait for us. In the main function, we use the {:?} formatting placeholder to print the debug information of the person variable.
#[derive(Debug)]struct Person { name: String, age: u8,}fn main() { let person = Person { name: "Mike".to_string(), age: 24, }; // Output: Person { name: "Mike", age: 24 } println!("{:?}", person);}
6. Struct Update Syntax
The struct update syntax allows us to specify only the fields that need to be changed when creating or modifying a struct instance, while the remaining fields retain their default or existing values. This syntax is particularly useful when a struct has many fields, and we only want to change one or a few of them.
#[derive(Debug)]struct Person { name: String, age: u8, city: String,}fn main() { let mut person = Person { name: "Mike".to_string(), age: 24, city: "London".to_string(), }; // All fields remain unchanged except for name person = Person { name: "Bob".to_string(), ..person }; // Output: Person { name: "Bob", age: 24, city: "London" } println!("{:?}", person);}
7. Using _ to Ignore Unwanted Values
When destructuring, we can use _ to ignore values we are not interested in.
fn main() { let (_, b) = (66, 99); // Output: 99 println!("{}", b);}
8. Using ? to Handle Result in Expressions
The ? operator can be used in expressions of type Result to automatically handle errors. If the Result is Err, the entire expression returns the error immediately. If it is Ok, execution continues.
use std::fs::File;use std::io::Read;fn read_file(path: &str) -> Result { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}fn main() { match read_file("example.txt") { Ok(contents) => println!("file contents: {}", contents), Err(error) => eprintln!("read file failed: {}", error), }}
9. Using break ‘label to Exit Nested Loops Early
By using labels (like ‘outer: and ‘inner:) before loops, we can use break ‘label to exit a specified labeled loop early in nested loops.
fn main() { 'outer: for i in 0..5 { 'inner: for j in 0..5 { if i == 2 && j == 2 { // Break out of the outer loop break 'outer; } println!("({}, {})", i, j); } }}
10. Deref Coercion
Deref coercion allows automatic dereferencing of types that implement the Deref Trait using the * operator, enabling access to their inner values. This is an implicit type conversion that makes the code more concise.
use std::ops::Deref;struct CustomBox { value: T,}impl Deref for CustomBox { type Target = T; fn deref(&self) -> &Self::Target { &self.value }}fn main() { let my_box = CustomBox { value: 66 }; // Through Deref coercion, we can directly dereference MyBox instance to access its inner value field println!("{}", *my_box);}