Hone logo
Hone
Problems

Implement a Future Trait for Asynchronous Operations

In Rust, asynchronous programming allows for efficient handling of operations that might take time, such as network requests or file I/O, without blocking the main thread. A core concept in this paradigm is the Future trait, which represents a value that may not be ready yet but will be at some point in the future. Your challenge is to implement a simplified version of this crucial trait.

Problem Description

Your task is to define and implement a Future trait in Rust. This trait will represent an asynchronous computation that can be polled to see if it has completed. When it completes, it should yield a result of a specific type.

You need to:

  1. Define the Future trait:
    • It should have an associated type Output which represents the type of the value the future will eventually produce.
    • It should have a single method, poll. This method takes a &mut Context as an argument and returns a Poll<Self::Output>.
    • The poll method is responsible for checking if the future has completed. If it has, it returns Poll::Ready(value). If it's not ready, it should return Poll::Pending.
  2. Define the Poll enum: This enum should have two variants:
    • Pending: Indicates that the future is not yet ready.
    • Ready(T): Indicates that the future has completed and produced a value of type T.
  3. Define the Context struct: For this challenge, the Context struct can be very simple. It doesn't need to hold any complex information, but it must be a struct that can be passed to poll.
  4. Implement Future for a simple type: Create a basic type (e.g., a struct that simulates a delayed computation) and implement the Future trait for it. This implementation should demonstrate how a future might transition from Pending to Ready.

Examples

Example 1: A Future that is immediately ready

// Assume the Future trait, Poll enum, and Context struct are defined

struct ImmediateFuture {
    value: i32,
}

impl Future for ImmediateFuture {
    type Output = i32;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        // This future is always ready
        Poll::Ready(self.value)
    }
}

// Usage (conceptual, as polling requires an executor)
// let mut future = ImmediateFuture { value: 42 };
// let mut context = Context {}; // Placeholder
// let result = future.poll(&mut Pin::new(&mut future), &mut context);
// assert_eq!(result, Poll::Ready(42));

Example 2: A Future that becomes ready after one poll

// Assume the Future trait, Poll enum, and Context struct are defined

struct DelayedFuture {
    value: String,
    ready: bool,
}

impl Future for DelayedFuture {
    type Output = String;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if !self.ready {
            self.ready = true;
            Poll::Pending
        } else {
            Poll::Ready(self.value.clone())
        }
    }
}

// Usage (conceptual)
// let mut future = DelayedFuture { value: "hello".to_string(), ready: false };
// let mut context = Context {}; // Placeholder
//
// let first_poll = future.poll(&mut Pin::new(&mut future), &mut context);
// assert_eq!(first_poll, Poll::Pending);
//
// let second_poll = future.poll(&mut Pin::new(&mut future), &mut context);
// assert_eq!(second_poll, Poll::Ready("hello".to_string()));

Constraints

  • The Future trait should be defined using standard Rust traits.
  • The Poll enum should be an enum.
  • The Context struct can be a simple, empty struct for this problem.
  • The poll method signature must match fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>. You'll need to understand Pin for this.
  • You do not need to implement an executor or a runtime to drive the futures; the focus is on defining and implementing the Future trait itself.

Notes

  • The Pin type in Rust is used to guarantee that a value will not be moved in memory. This is crucial for futures to ensure that Wakers (which are not explicitly part of this challenge but are fundamental to futures) remain valid. For this challenge, you can assume that Pin::new(&mut your_future_instance) is the way to get a Pin reference.
  • Think about how a real-world future would manage its state (e.g., whether it's pending or ready, and what value it will produce).
  • The cx: &mut Context<'_> argument to poll is intended to provide access to a Waker, which is used to signal to the executor that the future is ready to be polled again. For this simplified challenge, you won't be using the Waker directly, but its presence in the signature is important for correctness.
Loading editor...
rust