Start learning free

LESSON 662 OF 679 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Shapes

Practice O(n)

A drawing app keeps rectangles, squares and triangles in one list. Write the shape types so that code holding that list can ask any shape for its name and its perimeter (the length all the way around it) without knowing which kind of shape it is. That is polymorphism: one call, shape.perimeter(), and each kind of shape runs its own version.

Rust has no classes and no inheritance, so the common type is a trait: each struct implements it with impl Shape for ..., and a Box<dyn Shape> can hold any of them.

  • The trait Shape declares two methods: fn name(&self) -> String returns the kind of shape as text, and fn perimeter(&self) -> i32 returns the length around the shape.
  • Rectangle keeps a width and a height; Rectangle::new(width, height) builds one. Its name is "Rectangle", its perimeter 2 × (width + height).
  • Square keeps a side; Square::new(side) builds one. Its name is "Square", its perimeter 4 × side.
  • Triangle keeps three sides a, b and c; Triangle::new(a, b, c) builds one. Its name is "Triangle", its perimeter a + b + c.

Then write two functions that get the shapes as a &Vec<Box<dyn Shape>>:

  • total_perimeter(shapes) returns the sum of the perimeters of all the shapes.
  • largest_shape(shapes) returns the name of the shape with the largest perimeter. If several shapes share the largest perimeter, the one that comes first wins.

Both functions must work through the trait's methods only: never check which kind a shape is. Then a new kind of shape, added later, works with them unchanged.

The starter code already has every struct, every impl block and every method header: declare the trait's methods and the structs' fields, and write the bodies. For each test, the harness reads the shapes, builds them in the order given, calls total_perimeter and largest_shape, and prints the two results on one line, separated by a space.

 

Example 1:

Input:
    Rectangle::new(3, 4)
    Square::new(5)
    Triangle::new(3, 4, 5)
Output: 46 Square
Explanation: The perimeters are 2 × (3 + 4) = 14, 4 × 5 = 20 and 3 + 4 + 5 = 12. Their total is 46, and the square's 20 is the largest.

Example 2:

Input:
    Rectangle::new(2, 4)
    Square::new(3)
    Triangle::new(4, 4, 4)
Output: 36 Rectangle
Explanation: All three perimeters are 12: 2 × (2 + 4), 4 × 3 and 4 + 4 + 4. On a tie the shape that comes first in the list wins, so the answer is the rectangle.

Example 3:

Input:
    Square::new(1)
    Rectangle::new(1, 1)
    Triangle::new(2, 2, 3)
Output: 15 Triangle
Explanation: The perimeters are 4, 4 and 7, so the last shape, the triangle, is the largest. A 1 by 1 rectangle has the same sides as a square, but it is still a Rectangle: each object answers name() for its own class.

 

Constraints:

  • Every test has between 1 and 100 shapes: the list is never empty.
  • Every size (width, height, side, and each side of a triangle) is an integer from 1 to 104.
  • A triangle's three sides always make a real triangle: each side is shorter than the other two together.

Note:

Each function looks at every shape once: O(n) time for n shapes, and no extra space.

STEP 1 OF 23
    Ctrl + Enter to check