We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 1 of 26
Structs: naming your data
Define a Rect struct with a width and a height, then report its area and
its perimeter, each formatted to two decimal places and separated by one
space.
pub fn describe_rect(w: f64, h: f64) -> String
describe_rect(3.0, 4.0) is "12.00 14.00". Perimeter is 2 * (w + h).
Why a struct at all?
You could compute both numbers from w and h without ever naming a type.
The reason to make a struct is that Rect is a thing in your program’s
vocabulary, and once it exists the compiler starts helping you talk about it.
A function that takes Rect cannot be handed a (f64, f64) that happens to
be a position rather than a size.
A struct declaration is a layout plus a set of names:
struct Rect {
width: f64,
height: f64,
}
Three things about this that will surprise you if you come from almost any other language.
Every field must be given a value at construction. There is no null,
no undefined, no zero-initialised default. A struct literal that omits a
field is a compile error, E0063, and the message names the field you
forgot. The starter ships exactly that error — read it before you fix it.
The consequence is large: a Rect value that exists is a complete Rect.
There is no “half-built object” state anywhere in the language, so no
function ever has to defend against one.
There is no field-level mut. Mutability lives on the binding, not on
the type. let r = Rect { .. } gives you a rectangle nobody can change;
let mut r = Rect { .. } makes every field writable. If you want one field
fixed and another writable, you express that with privacy and methods, not
with a keyword on the field. Try assigning through an immutable binding and
you will meet E0594.
Fields are accessed with ., and a typo is a compile error (E0609,
“no field wdith on type Rect“), not a runtime undefined.
Owning your data
Notice the fields are f64 — plain owned values. Keep it that way for now.
A struct that stores a &str or a &[T] is borrowing from somewhere else,
and Rust will demand you say how long that loan lasts with a lifetime
parameter (E0106, “missing lifetime specifier”). That is a whole track of
its own later. Until then: every struct you write owns what it holds —
String, not &str; Vec<T>, not &[T].
Where to put the arithmetic
You can compute the area inline, but the idiomatic move is an impl block:
impl Rect {
fn area(&self) -> f64 {
self.width * self.height
}
}
&self means “I only want to read this rectangle” — the caller keeps it and
can use it again afterwards. Methods get a full item of their own next; here
they are just a tidier place to put two formulas.
Formatting
format!("{:.2}", x) rounds to two decimals. Two values with a space between
them is one format! call, not string concatenation.
Notes on the gate
Two lints hover over struct code. redundant_field_names fires when you
write Rect { width: width } where Rect { width } would do — field-init
shorthand is not optional style here, it is enforced. struct_field_names
complains when every field repeats the struct’s name (rect_width,
rect_height) — the type already provides that namespace.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.