diff --git a/Cargo.toml b/Cargo.toml index 6cbb58e..8428a85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ edition = "2018" crossbeam-channel = "0.4.4" log = { version = "0.4.6", features = ["std"] } time = "0.1.35" +lazy_format = "2.0.3" diff --git a/README.md b/README.md index a133d65..8868f01 100644 --- a/README.md +++ b/README.md @@ -45,3 +45,41 @@ fn main() { ``` More examples can be found under `examples` directory. + +## Handling Owned Formatting Arguments + +The standard `std::fmt::Arguments` type in Rust is tied to the lifetime of the data being formatted. This makes it unsuitable for scenarios where the formatted arguments need to be stored or sent across threads (i.e., require a `'static` lifetime). + +For these situations, you can use the `lazy_format` crate. It allows you to create formatting objects that capture their arguments (by moving or cloning them). If all captured arguments are themselves owned and `'static`, the resulting `lazy_format` object can also be `'static`. + +**Example:** + +```rust +use lazy_format::lazy_format; +use std::fmt::Display; +use std::thread; + +fn create_owned_formatter(name: String, count: i32) -> Box { + Box::new(lazy_format!("User: {}, Count: {}", name, count)) +} + +fn main() { + let name = String::from("Alice"); + let formatter = create_owned_formatter(name, 42); + + let handle = thread::spawn(move || { + // This formatter can be sent to another thread + println!("{}", formatter); // Outputs: "User: Alice, Count: 42" + }); + handle.join().unwrap(); +} +``` + +First, add `lazy_format` to your `Cargo.toml`: + +```toml +[dependencies] +lazy_format = "2.0.3" # Use the latest version +``` + +This approach provides a flexible way to handle formatting needs where lifetimes would otherwise be an issue. diff --git a/examples/owned_format.rs b/examples/owned_format.rs new file mode 100644 index 0000000..88cc2b8 --- /dev/null +++ b/examples/owned_format.rs @@ -0,0 +1,28 @@ +use lazy_format::lazy_format; +use std::fmt::Display; +use std::thread; + +fn main() { + let owned_string = String::from("world"); + let number = 42; + + // Create a lazy_format object that captures owned data. + // This object itself should be Send + 'static. + let formatter: Box = + Box::new(lazy_format!("Hello, {}! The number is {}.", owned_string, number)); + + // Demonstrate that it can be sent to another thread. + let handle = thread::spawn(move || { + // Format the object in the other thread. + let formatted_string = formatter.to_string(); + println!("From thread: {}", formatted_string); + formatted_string + }); + + let result_from_thread = handle.join().expect("Thread panicked"); + + println!("In main thread, got back: {}", result_from_thread); + + assert_eq!(result_from_thread, "Hello, world! The number is 42."); + println!("Successfully created and used an owned formatter across threads!"); +} diff --git a/tests/owned_format_test.rs b/tests/owned_format_test.rs new file mode 100644 index 0000000..ec164fe --- /dev/null +++ b/tests/owned_format_test.rs @@ -0,0 +1,60 @@ +use lazy_format::lazy_format; +use std::fmt::Display; +use std::thread; + +#[test] +fn test_owned_format_across_threads() { + let owned_string = String::from("Rustacean"); + let value = 100; + + // Create a lazy_format object capturing owned data. + // Ensure it's Send + 'static by boxing it. + let formatter: Box = + Box::new(lazy_format!("Greetings, {}! Your score is {}.", owned_string, value)); + + // Move to another thread and format. + let handle = thread::spawn(move || { + formatter.to_string() + }); + + let result_from_thread = handle.join().expect("Thread panicked during test"); + + assert_eq!(result_from_thread, "Greetings, Rustacean! Your score is 100."); +} + +#[test] +fn test_owned_format_static_lifetime() { + // This test demonstrates that if all inputs are 'static, the formatter can be 'static. + fn create_static_formatter() -> Box { + Box::new(lazy_format!("This is a static message from {}.", "static data")) + } + + let formatter = create_static_formatter(); + + // Move to another thread + let handle = thread::spawn(move || { + formatter.to_string() + }); + + let result = handle.join().unwrap(); + assert_eq!(result, "This is a static message from static data."); +} + +#[test] +fn test_format_with_owned_moved_value() { + let my_data = String::from("owned and moved"); + + // my_data is moved into the formatter + let formatter: Box = + Box::new(lazy_format!("Data: {}", my_data)); + + // If my_data was not moved, this would be a compile error: + // drop(my_data); // uncommenting this should cause a compile error if lazy_format doesn't take ownership + + let handle = thread::spawn(move || { + formatter.to_string() + }); + + let result = handle.join().unwrap(); + assert_eq!(result, "Data: owned and moved"); +}