( )Punctuation
Explanation
( ) serves several distinct roles depending on context:
- Grouping:
(a + b) * c— overrides normal precedence, same as in arithmetic notation generally. - Tuple expression/type:
(1, "a", true)is a 3-tuple value;(i32, &str, bool)is its type.()with nothing inside is the unit value/type — Rust's "no meaningful value" type, distinct fromvoidin that it's a real, first-class, zero-sized type you can bind, pass, and return. - Single-element tuple:
(x,)— the trailing comma is mandatory (see,); without it,(x)is justxgrouped, not a tuple. - Function call / tuple-struct or enum-variant construction:
f(a, b),Point(1, 2),Some(x).
Which meaning applies depends on position and content: ( directly
after any expression (a name, a call's result, an indexed element) is a
call or construction; ( starting an expression is grouping or a tuple —
and between those two, the presence of a comma inside is what makes it a
tuple.
Usage examples
Grouping, calling, and building tuples
fn add(a: i32, b: i32) -> i32 { a + b } // <- `( )` groups the parameter list
let sum = add(1, 2); // <- `( )` here is the call, passing the arguments
let pair = (1, "a"); // <- `( )` here builds a tuple value
Creating a new object
Tuple structs and enum variants are constructed by calling their name
like a function — the same ( ) syntax as any other function call,
which is what makes Some/Ok/a tuple struct usable directly as a
function value in iterator adaptors.
struct Meters(f64);
let distance = Meters(4.2); // <- `( )` here constructs a tuple struct
let readings: Vec<Option<f64>> = vec![1.0, 2.0]
.into_iter()
.map(Some) // <- `Some` used directly as a fn value, thanks to this same `( )` construction rule
.collect();
Because tuple-struct/variant construction really is a
function call under the hood, the constructor can be passed anywhere a
closure is expected (.map(Some) instead of .map(|x| Some(x))) — one
less closure to write and read.
Branching on data (pattern matching)
The same ( ) used to construct a tuple struct or variant is used, in
reverse, to destructure one inside a match arm or if let.
enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
let shape = Shape::Circle(2.0); // <- `( )` constructs the variant...
let area = match shape {
Shape::Circle(r) => std::f64::consts::PI * r * r, // <- ...and `( )` destructures it again here
Shape::Rectangle(w, h) => w * h,
};
Matching gives each field a name at the point of use
(r, w, h) instead of reaching into the value with .0/.1
afterward — the
Rust Book covers
this destructuring-in-match pattern for working with enum payloads.