Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 1 of 26

Easy Primitives

Structs: naming your data

Complete the construction of a Rect from its width and height. Return its area and perimeter as text, each with two decimal places, separated by one space. Do not add a newline.

pub fn describe_rect(w: f64, h: f64) -> String

Use the supplied Rect type and its methods. The area is w * h, and the perimeter is 2.0 * (w + h). Inputs are finite, nonnegative dimensions small enough that both calculations remain finite. Zero dimensions are allowed.

Width Height Result
3.0 4.0 "12.00 14.00"
1.5 2.5 "3.75 8.00"
0.0 5.0 "0.00 10.00"

Give related values a type

struct Rect {
    width: f64,
    height: f64,
}

Named fields make the two dimensions easier to identify than unnamed tuple elements. Rect is also a distinct type: a function expecting it will not accept an arbitrary (f64, f64) pair. The type alone does not validate the dimensions, so this exercise states the allowed inputs separately.

Read the missing-field error

The starter constructs Rect { width: w }, leaving out height. Rust reports E0063 because this literal does not supply every field. Add the height from the function's h argument.

This literal does not automatically give omitted fields zero values. Rust also has explicit ways to obtain fields from another value, such as struct update syntax, and types can implement Default. Neither is needed here.

Access a field with rect.width. A misspelling such as rect.wdith is a compile-time error (E0609). There is no field-level mut keyword. For this struct's plain f64 fields, assigning directly through a local binding requires that binding to be mutable. let rect = ... is sufficient for this task because its methods only read the dimensions.

Mutability is not a permanent property attached to a value: it can be moved into a mutable binding later. Other field types, such as Cell, can provide interior mutability, but this exercise does not need it.

Read-only methods

The supplied methods live in an impl Rect block:

impl Rect {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

&self borrows the rectangle without taking ownership. The caller can use the same rectangle for both area() and perimeter(). Its fields store numeric values directly, so this type needs no lifetime parameter. Structs that store references can be useful too; those references need an explicit lifetime, which is covered later.

Format the result

format!("{:.2} {:.2}", area, perimeter) produces one string with exactly two digits after each decimal point and one space between the values. Formatting rounds the computed floating-point values. It does not make the underlying arithmetic exact: for example, 0.1 * 0.2 is not represented exactly as the decimal number 0.02, but formats as "0.02" here.

When a variable has the same name as a field, Rect { width, height } is shorthand for naming those values explicitly. Here the parameters are called w and h, so width: w and height: h are appropriate.

Check the examples and the empty-size case (0.0, 0.0). Passing requires compilation, correct output and Clippy with warnings treated as errors.