Hone logo
Hone
Problems

Rust Ownership and Borrowing Basics: The Library System

This challenge focuses on understanding Rust's ownership system, a core concept for memory safety and preventing data races. You'll simulate a simplified library system where books can be checked out and returned, demonstrating how ownership and borrowing work in practice. Successfully completing this challenge will solidify your grasp of Rust's fundamental memory management rules.

Problem Description

You are tasked with creating a simple library system in Rust. The system should manage a collection of Book structs. Each Book has a title (String). The core functionality involves:

  1. Adding Books: A function to add new books to the library.
  2. Checking Out Books: A function to "check out" a book from the library. When a book is checked out, ownership of the Book is transferred to the borrower. The library's record of the book should be removed.
  3. Returning Books: A function to "return" a checked-out book to the library. When a book is returned, ownership is transferred back to the library, and the book is added back to the library's collection.

The challenge emphasizes correct ownership transfer and borrowing to avoid common Rust errors like double-freeing or use-after-move. You must ensure that the library always has a valid record of the books it owns, and that checked-out books are not accessible from the library while they are checked out.

Examples

Example 1:

Input:
library: Library { books: ["The Lord of the Rings"] }
book_title: "The Lord of the Rings"

Output:
library: Library { books: [] }
borrowed_book: Some(Book { title: "The Lord of the Rings" })

Explanation: The book "The Lord of the Rings" is checked out. Ownership is transferred from the library to the borrower. The library's books vector is now empty.

Example 2:

Input:
library: Library { books: [] }
borrowed_book: Some(Book { title: "Pride and Prejudice" })

Output:
library: Library { books: ["Pride and Prejudice"] }
borrowed_book: None

Explanation: The borrowed book "Pride and Prejudice" is returned. Ownership is transferred back to the library. The library's books vector now contains "Pride and Prejudice". The borrowed_book is now None.

Example 3: (Edge Case - Book Not Found)

Input:
library: Library { books: ["The Hobbit"] }
book_title: "The Silmarillion"

Output:
library: Library { books: ["The Hobbit"] }
borrowed_book: None

Explanation: Attempting to check out a book that doesn't exist in the library results in no changes to the library and a None value for the borrowed book.

Constraints

  • The Book struct should have a title field of type String.
  • The Library struct should contain a books field, which is a Vec<Book>.
  • The checkout_book and return_book functions should handle the case where the book is not found in the library gracefully (return None for the borrowed book in this case).
  • The code must compile and run without errors or warnings.
  • The solution should demonstrate a clear understanding of ownership and borrowing rules. Avoid unnecessary cloning.

Notes

  • Consider using Option<Book> to represent the possibility of a book not being found or borrowed.
  • Think carefully about when ownership needs to be transferred and when borrowing is sufficient.
  • Rust's compiler will be your friend (and sometimes your enemy!). Pay close attention to the error messages.
  • The goal is to write clean, idiomatic Rust code that adheres to the principles of ownership and borrowing.
  • You don't need to implement any user interface or file I/O. Focus solely on the core logic of ownership and borrowing.
  • Assume that the Book struct is already defined. You only need to implement the library functions.
#[derive(Debug)]
struct Book {
    title: String,
}

#[derive(Debug)]
struct Library {
    books: Vec<Book>,
}

impl Library {
    fn new() -> Self {
        Library { books: Vec::new() }
    }

    fn add_book(&mut self, title: String) {
        self.books.push(Book { title });
    }

    fn checkout_book(&mut self, title: &str) -> Option<Book> {
        if let Some(index) = self.books.iter().position(|book| book.title == title) {
            let book = self.books.remove(index);
            Some(book)
        } else {
            None
        }
    }

    fn return_book(&mut self, book: Book) {
        self.books.push(book);
    }
}
Loading editor...
rust