Implementing Context.WithValue for Enhanced Request Handling
Context.WithValue in Go is a powerful tool for propagating request-specific data through function calls without explicitly passing it as arguments. This challenge asks you to implement a simplified version of context.WithValue to understand its underlying mechanics and how it facilitates cleaner, more maintainable code, particularly in scenarios like web request handling where you need to pass information like user IDs or request IDs across multiple middleware functions.
Problem Description
You are tasked with creating a WithValue function that mimics the core functionality of context.WithValue. This function should take a context and a key (of type interface{}) and a value (also of type interface{}) and return a new context with the key-value pair associated with it. The new context should be distinct from the original; modifying the value in the new context should not affect the original context. You also need to implement a Value function that retrieves the value associated with a given key from a context. If the key is not found, Value should return nil.
Key Requirements:
- Immutability: The original context should remain unchanged.
WithValuemust return a new context. - Key-Value Association: The new context should store the provided key-value pair.
- Value Retrieval: The
Valuefunction should correctly retrieve the value associated with a key. - Nil Handling: If a key is not found in the context,
Valueshould returnnil. - Type Safety (Limited): While the key and value are
interface{}, the implementation should be designed to avoid unexpected type assertions or panics.
Expected Behavior:
- Calling
WithValuewith a context, key, and value should return a new context containing the key-value pair. - Calling
Valueon the new context with the same key should return the provided value. - Calling
Valueon the original context (beforeWithValuewas called) with the same key should returnnil. - Calling
Valueon any context with a different key should returnnil.
Edge Cases to Consider:
- Nil context: Handle the case where the input context is
nilgracefully (return a new context with the key-value pair). - Duplicate keys: The last value associated with a key should overwrite any previous values.
- Type assertions: Be mindful of potential type assertion errors when retrieving values.
Examples
Example 1:
Input: context := context.Background(); key := "user_id"; value := 123
Output: newContext := WithValue(context, key, value); retrievedValue := Value(newContext, key)
Explanation: `WithValue` creates a new context. `Value` retrieves the value 123 from the new context.
Example 2:
Input: context := context.Background(); key := "request_id"; value := "abc-123"; newContext := WithValue(context, key, value); retrievedValue := Value(newContext, key)
Output: retrievedValue == "abc-123"
Explanation: Demonstrates the use of a string value.
Example 3: (Edge Case - Nil Context)
Input: context := nil; key := "some_key"; value := 42
Output: newContext := WithValue(context, key, value); retrievedValue := Value(newContext, key)
Explanation: Handles the case where the initial context is nil.
Constraints
- The
WithValuefunction must not modify the original context. - The
Valuefunction must returnnilif the key is not found. - The implementation should be reasonably efficient (avoid unnecessary allocations).
- The key and value types are
interface{}. - The solution should be concise and readable.
Notes
Consider using a map internally to store the key-value pairs associated with the context. Remember that contexts are often used in concurrent scenarios, so be mindful of potential race conditions if you were to extend this implementation further (though this is not a requirement for this challenge). Focus on the core functionality of associating and retrieving values. The goal is to understand the fundamental concept behind context.WithValue.