Start learning free

LESSON 564 OF 576 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Inventory Item in Rust: Private Stock and a Public Name

Practice O(1) Read the full explanation

A shop keeps track of how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is a private field, changed only through two methods that check the rules. The item's name has no rules to protect, so any code can read it.

As in the Access Modifiers lesson, a Rust struct field is private by default, and pub makes it visible to other modules. Write:

  • The InventoryItem struct, with a public field pub name (a String) and a private field stock (an i32).
  • InventoryItem::new(name, stock): returns an item with that name and that many pieces in stock.
  • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock by n and returns true. Otherwise it changes nothing and returns false: selling 0 pieces, a negative number of pieces, or more pieces than there are in stock is refused.
  • restock(n) adds n pieces to the stock when n is at least 1, and ignores n otherwise.
  • get_stock() returns the stock. It is the only way for other code to read it.

sell and restock change the item, so they take &mut self; get_stock only reads, so it takes &self. The starter code already has these headers.

Each test case creates one item with InventoryItem::new, then applies a list of operations to it, in order. The output has one line per test case: the item's name and a colon, then what each sell and get_stock call returns, separated by spaces, with sell's answer printed as True or False.

 

Example 1:

Input:
    InventoryItem::new("Pencil", 5)
    sell(3)
    get_stock()
    sell(5)
    get_stock()
    restock(10)
    get_stock()
Output: Pencil: True 2 False 2 12
Explanation: Selling 3 of the 5 pencils works and leaves 2. Selling 5 more is refused, because only 2 are left,
and the stock stays 2. restock(10) brings it to 12.

Example 2:

Input:
    InventoryItem::new("Mug", 4)
    sell(4)
    get_stock()
    sell(1)
    get_stock()
Output: Mug: True 0 False 0
Explanation: Selling all 4 mugs is allowed and leaves 0. With nothing left, even one mug can't be sold.

Example 3:

Input:
    InventoryItem::new("Lamp", 2)
    sell(0)
    sell(-3)
    restock(0)
    restock(-5)
    get_stock()
    restock(1)
    sell(3)
    get_stock()
Output: Lamp: False False 2 True 0
Explanation: Selling 0 or -3 lamps is refused, and restocking 0 or -5 lamps is ignored, so the stock is still 2.
After restock(1) there are 3 lamps, and selling all 3 works.

 

Constraints:

  • A name is one word of English letters. 0 <= stock <= 104 when the item is created.
  • -104 <= n <= 104 in every sell and restock.
  • A test case has from 1 to 1000 operations, and at least one of them is a sell or a get_stock.
STEP 1 OF 7
    Ctrl + Enter to check

    RUST · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Inventory Item in Rust, explained

    Problem overview

    A shop tracks how many pieces of each item it has: the item's stock. The stock can never go below 0, and it changes only when pieces are sold or new ones arrive. So the stock is a private field, changed only through two methods that check the rules. The item's name has no rules to protect, so any code can read it.

    You write the InventoryItem struct with a public field pub name (a String) and a private field stock (an i32), and these functions:

    • InventoryItem::new(name, stock) returns an item with that name and that many pieces.
    • sell(n) sells n pieces. If n is at least 1 and at most the stock, it lowers the stock by n and returns true. Otherwise it changes nothing and returns false.
    • restock(n) adds n pieces when n is at least 1, and ignores n otherwise.
    • get_stock() returns the stock. It is the only way for other code to read it.

    For a pencil with a stock of 5, sell(3) works and leaves 2. Then sell(5) is refused, because only 2 are left, and the stock stays 2. After restock(10) it is 12.

    Approach

    There are two rules, one per method, and each is a check before a change:

    • Selling is refused when n is below 1 (nothing or a negative number of pieces) or above the stock (more than the shop has). Selling exactly the whole stock is allowed. Because the check comes before any change, a refused sale leaves the stock exactly as it was.
    • Restocking adds only when n is at least 1. A delivery of 0 or a negative number isn't a delivery, so it is ignored.

    Rust fields are private by default: without pub, code in other modules can't read or change them. That is how stock is protected. The only way to touch it from outside is through sell and restock, and the only way to see it is get_stock. The name is marked pub, so other code can read and even change it directly.

    Step by step

    1. Declare the fields: pub name: String and, below it, stock: i32 without pub.
    2. Build the item in new. The parameter name is a &str, so name.to_string() makes the owned String that the struct stores. The stock comes straight from the parameter.
    3. In sell, refuse first. An if n < 1 || n > self.stock whose body returns false. The || means "or": either condition is enough to refuse.
    4. Then sell. Only a sale that passed the check reaches the next line, self.stock -= n;, so the stock never goes below 0. End with true, without a semicolon.
    5. In restock, guard the addition: if n >= 1 { self.stock += n; }.
    6. get_stock returns self.stock. It only reads, so it takes &self. The methods that change the item take &mut self.

    The solution in Rust

    struct InventoryItem {
        pub name: String,
        stock: i32,
    }
    impl InventoryItem {
        fn new(name: &str, stock: i32) -> InventoryItem {
            InventoryItem {
                name: name.to_string(),
                stock: stock,
            }
        }
        fn sell(&mut self, n: i32) -> bool {
            if n < 1 || n > self.stock {
                return false;
            }
            self.stock -= n;
            true
        }
        fn restock(&mut self, n: i32) {
            if n >= 1 {
                self.stock += n;
            }
        }
        fn get_stock(&self) -> i32 {
            self.stock
        }
    }
    

    Using it with a pencil:

    let mut p = InventoryItem::new("Pencil", 5);
    println!("{}", p.sell(3));
    println!("{}", p.get_stock());
    println!("{}", p.sell(5));
    println!("{}", p.get_stock());
    p.restock(10);
    println!("{} {}", p.name, p.get_stock());
    

    It prints:

    true
    2
    false
    2
    Pencil 12
    

    Rust details worth knowing:

    • The early return. return false; leaves sell at once. When the check passes, the function goes on to the last line, true, which has no semicolon and is the returned value.
    • pub on a field. From another module, item.name can be read and assigned. For stock, reading it gives the error "field stock of struct InventoryItem is private". That is why get_stock exists.
    • stock: stock. This works, but it is longer than the field init shorthand, stock alone. Both mean the same.
    • Signed numbers. The parameter n is an i32, so negative values can be passed, which is why the check n < 1 matters. Because the check runs first, the subtraction can never take the stock below 0.

    Complexity

    Each method does a couple of comparisons and at most one update, so every call is O(1) time, and the struct uses O(1) space.

    Edge cases and common mistakes

    • Selling everything. For a mug with a stock of 4, sell(4) returns true and leaves 0. With nothing left, sell(1) returns false. Writing n >= self.stock as the refusal test would wrongly refuse the first one.
    • Selling 0 or a negative number. Both are refused with false and the stock doesn't change. A negative sale would otherwise raise the stock.
    • Restocking 0 or a negative number. Both are ignored. A lamp with a stock of 2 still has 2 after restock(0) and restock(-5).
    • Changing before checking. Subtracting first and then testing the result leaves the stock changed when the sale is refused.
    • Returning something from restock. It returns nothing, so there is no true or false to produce.

    Testing

    • A mug with a stock of 4: sell(4) gives true, get_stock() gives 0, sell(1) gives false, get_stock() gives 0.
    • A lamp with a stock of 2: sell(0) and sell(-3) give false, and after restock(0) and restock(-5) the stock is still 2.
    • Continuing with the lamp, restock(1) makes the stock 3, so sell(3) gives true and the stock is 0.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free