Hone logo
Hone
Problems

Resource Management with Scope Guards in Rust

Scope guards are a powerful technique in Rust for ensuring cleanup actions are performed when a scope ends, regardless of how the scope exits (normally or due to a panic). This challenge will guide you in implementing a simple scope guard system to automatically release resources like file handles or locks. Understanding and utilizing scope guards is crucial for writing robust and reliable Rust code.

Problem Description

You are tasked with creating a basic scope guard system. The system should allow you to register a closure (a function without a name) that will be executed when a specific scope ends. This closure will represent the cleanup action. The scope is defined by a block of code enclosed in curly braces {}. The scope guard system should ensure that the cleanup closure is always called, even if a panic occurs within the scope.

Specifically, you need to implement a ScopeGuard struct with the following functionality:

  1. new(cleanup: impl FnOnce()) -> Self: A constructor that takes a closure cleanup as an argument. This closure will be executed when the scope ends. FnOnce is used because the closure might consume captured variables.
  2. drop(self): The drop method, which is automatically called when the ScopeGuard instance goes out of scope. This method should execute the registered cleanup closure.

You will also need to create a simple test case to demonstrate that the cleanup closure is indeed called when the ScopeGuard goes out of scope, even if a panic is triggered within the scope. The test case should print a message before and after the scope guard's drop method is called.

Examples

Example 1:

Input: A simple scope with a ScopeGuard that prints "Cleanup!".
Output: "Before Scope", "Cleanup!", "After Scope"
Explanation: The "Before Scope" message is printed before the scope begins. The ScopeGuard is created.  The "Cleanup!" message is printed when the ScopeGuard's `drop` method is called as the scope ends. The "After Scope" message is printed after the scope ends.

Example 2:

Input: A scope with a ScopeGuard and a panic within the scope.
Output: "Before Scope", "Cleanup!", "After Scope"
Explanation: The "Before Scope" message is printed. The ScopeGuard is created. A panic is triggered. The `drop` method of the ScopeGuard is automatically called by Rust's panic handling mechanism before unwinding. The "Cleanup!" message is printed. The "After Scope" message is printed after the panic is handled.

Example 3: (Edge Case - Nested Scopes)

Input: Nested scopes with ScopeGuards.
Output: "Before Outer Scope", "Cleanup Inner!", "Cleanup Outer!", "After Outer Scope"
Explanation: The "Before Outer Scope" message is printed. An inner scope is entered, and an inner ScopeGuard is created. The inner scope ends, and its ScopeGuard's `drop` method is called, printing "Cleanup Inner!". The outer scope ends, and its ScopeGuard's `drop` method is called, printing "Cleanup Outer!". The "After Outer Scope" message is printed.

Constraints

  • The cleanup closure must be FnOnce.
  • The scope guard system must correctly execute the cleanup closure even in the presence of panics.
  • The solution must be idiomatic Rust.
  • The solution should be concise and readable.

Notes

  • Consider using Rust's panic unwinding mechanism to ensure the cleanup closure is called during panic handling.
  • The drop method is automatically called when a value goes out of scope, either normally or due to a panic.
  • Think about how to structure your code to make it easy to register and manage cleanup actions.
  • The test case is crucial to verify the correctness of your implementation, especially in the presence of panics. Use #[test] and assert! to create a robust test.
// Your code here
Loading editor...
rust