Start learning free

LESSON 255 OF 576 · FUNCTIONS & CLASSES

Rectangle Class in Rust: A Struct with Methods

Practice O(1) Read the full explanation

A class bundles some values with the questions you can ask about them. Write a class Rectangle that remembers a rectangle's width and height, and can tell its area, its perimeter, and whether it is a square.

Implement the class Rectangle:

  • Rectangle(width, height) creates a rectangle with the given sizes.
  • area() returns the area of the rectangle: its width times its height.
  • perimeter() returns the perimeter: the length all the way around, twice the width plus twice the height.
  • isSquare() returns true if the width and the height are equal, and false otherwise.

Each line of the input is one rectangle: its width and its height. The test code first creates a Rectangle for every line, then asks each of them, in order, for its area, its perimeter and whether it is a square, and prints the three answers on one line, the boolean as True or False. So each object must keep its own sizes. The starter code spells these names the way your language does (in Go and Rust, Rectangle is a struct with methods).

 

Example 1:

Input:
    Rectangle(3, 4)
Output: 12 14 False
Explanation: The area is 3 · 4 = 12. Going around, the sides are 3 + 4 + 3 + 4 = 14.
The width and the height differ, so it isn't a square.

Example 2:

Input:
    Rectangle(5, 5)
Output: 25 20 True
Explanation: 5 · 5 = 25, and 4 sides of 5 make 20. Both sizes are 5: a square is a rectangle with equal sides.

Example 3:

Input:
    Rectangle(2, 10)
    Rectangle(10, 2)
Output:
20 24 False
20 24 False
Explanation: Two objects, each with its own sizes. Turning a rectangle on its side changes neither its area nor its perimeter.

 

Constraints:

  • 1 <= width, height <= 104
  • At most 10 rectangles per test.

Note:

Each method needs only the two sizes, so each one runs in O(1) time.

STEP 1 OF 5
    Ctrl + Enter to check

    RUST · FUNCTIONS & CLASSES

    Rectangle Class in Rust, explained

    Problem overview

    A struct bundles some values together, and an impl block adds the questions you can ask about them. In this lesson, Rectangle remembers a width and a height, and answers three questions: its area (width times height), its perimeter (twice the width plus twice the height) and whether it is_square (width and height are equal).

    Every Rectangle value keeps its own sizes. A 3 by 4 rectangle has area 12, perimeter 14 and is not a square. A 5 by 5 rectangle has area 25, perimeter 20 and is a square. A 2 by 10 and a 10 by 2 rectangle have the same area and perimeter, because turning a rectangle on its side changes neither.

    Approach

    Nothing needs to be computed ahead of time. The struct stores only the two sizes, and each method works its answer out from them when it is called. There are no loops, so every method runs in constant time.

    Rust has no classes. A struct holds the data, and the methods live in a separate impl Rectangle block. Two kinds of function appear in it:

    • new has no self parameter. It is called on the type, as Rectangle::new(3, 4), and it builds a value. Rust has no special constructor syntax, so new is just a function that returns a Rectangle, by convention.
    • area, perimeter and is_square take &self. That is a read-only borrow of the rectangle the method was called on, which is all they need, since they only look at the fields.

    Step by step

    1. Declare the fields. width and height, both i32, separated by commas. Each rectangle value carries its own copy of them.
    2. Build the value in new. A struct literal is enough. Because the parameters have the same names as the fields, Rectangle { width, height } is short for Rectangle { width: width, height: height }. With no semicolon, it is the value the function returns.
    3. Area. Multiply self.width by self.height.
    4. Perimeter. Walking around the rectangle covers each side once, which is width + height + width + height. Write it as 2 * (self.width + self.height), with the parentheses so the addition happens first.
    5. Square test. self.width == self.height is already a bool, so no if is needed.

    The solution in Rust

    struct Rectangle {
        width: i32,
        height: i32,
    }
    impl Rectangle {
        fn new(width: i32, height: i32) -> Rectangle {
            Rectangle { width, height }
        }
        fn area(&self) -> i32 {
            self.width * self.height
        }
        fn perimeter(&self) -> i32 {
            2 * (self.width + self.height)
        }
        fn is_square(&self) -> bool {
            self.width == self.height
        }
    }
    

    Using it looks like this:

    let a = Rectangle::new(3, 4);
    println!("{} {} {}", a.area(), a.perimeter(), a.is_square());
    let b = Rectangle::new(5, 5);
    println!("{} {} {}", b.area(), b.perimeter(), b.is_square());
    let c = Rectangle::new(2, 10);
    let d = Rectangle::new(10, 2);
    println!("{} {} {}", c.area(), c.perimeter(), c.is_square());
    println!("{} {} {}", d.area(), d.perimeter(), d.is_square());
    

    It prints:

    12 14 false
    25 20 true
    20 24 false
    20 24 false
    

    The lesson's tests print the boolean as True or False. Rust's own println! spells it true and false.

    Some Rust details worth noting:

    • The last expression is the result. Each method body is a single expression with no semicolon. Adding a semicolon turns it into a statement, the function then returns (), and the compiler reports a type mismatch.
    • Methods are called with a dot. a.area() passes a as &self automatically, so you never write the borrow yourself.
    • Fields are private to the module. The tests only go through the methods, so nothing here needs pub.

    Edge cases and common mistakes

    • Forgetting the parentheses in the perimeter. 2 * self.width + self.height multiplies only the width. For a 3 by 4 rectangle it gives 10 instead of 14.
    • Sizes that don't fit in i32. The sizes go up to 10,000, so the largest area is 100,000,000 (for 10,000 by 10,000, with a perimeter of 40,000), far below the i32 limit of about 2.1 billion.
    • Mixing up the two sizes. Swapping width and height changes nothing the methods report, so a test with swapped sizes can't catch that mistake.
    • Squares. A square is a rectangle whose sides are equal, so 5 by 5 gives true. A 2 by 10 rectangle gives false, whichever way round the sizes are given.
    • Storing the answers. Keeping area as a field would be one more value to keep in step with the sizes. Computing it in a method can't go out of date.

    Testing

    A few small rectangles worth trying, with the answers the methods give:

    • Rectangle::new(3, 4) gives area 12, perimeter 14 and false.
    • Rectangle::new(5, 5) gives area 25, perimeter 20 and true.
    • Rectangle::new(1, 1) is the smallest allowed rectangle: area 1, perimeter 4, and a square.
    • Rectangle::new(10000, 10000) is the largest: area 100000000, perimeter 40000, and a square.

    Two values made one after the other don't interfere. After let c = Rectangle::new(2, 10); and let d = Rectangle::new(10, 2);, the value c still has width 2 and height 10. Each struct value owns its own two numbers, and a method call reads the numbers of the value in front of the dot.

    The same impl block serves every rectangle. Only the data is separate for each value, which is why the methods take &self: it tells them which rectangle to read. Try it in the editor above, with the sizes from the examples.

    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free