Files
announcement/examples/01_basic_usage.rs
T
swaits e8e55b7676 chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
2025-10-13 19:36:37 -06:00

169 lines
6.9 KiB
Rust

//! ============================================================================
//! LEARNING GUIDE - PART 1: Basic Usage
//! ============================================================================
//!
//! WHAT YOU'LL LEARN:
//! - How to create an announcement channel
//! - How to announce a value
//! - How to check for values without blocking (try_listen)
//! - Basic synchronous usage patterns
//!
//! PREREQUISITES:
//! - None - start here!
//!
//! KEY CONCEPTS:
//! - Announcement channel: One sender (Announcer), many receivers (Listeners)
//! - Oneshot semantics: Can only announce ONCE
//! - Non-blocking checks: Use try_listen() to check without waiting
//!
//! Run with: cargo run --example 01_basic_usage
use announcement::Announcement;
fn main() {
println!("=== Basic Announcement Usage ===\n");
// STEP 1: Create an announcement channel
// ========================================
// The new() method returns TWO things:
// - announcer: Used ONCE to broadcast a value
// - announcement: Used to create listeners that receive the value
//
// IMPORTANT: The announcer is NOT Clone - you can only use it once!
// This enforces the "oneshot" semantics at compile time.
let (announcer, announcement) = Announcement::<i32>::new();
println!("Step 1: Created announcement channel");
println!(" - announcer: Will send the value (used once)");
println!(" - announcement: Creates listeners (can create many)\n");
// STEP 2: Create a listener BEFORE announcing
// ============================================
// Listeners can be created at any time - before OR after the value is announced.
// Each listener is independent and will receive the same value.
//
// NOTE: Creating a listener is very cheap (~20ns, just 2 Arc clones).
let listener1 = announcement.listener();
println!("Step 2: Created first listener");
println!(" - Listener created BEFORE value is announced\n");
// STEP 3: Try to listen (non-blocking check)
// ===========================================
// try_listen() checks if a value has been announced WITHOUT waiting.
// Returns:
// - Some(value) if announced
// - None if not yet announced
//
// IMPORTANT: This is a lock-free operation (~5-10ns), very fast!
match listener1.try_listen() {
Some(value) => {
println!("Step 3: Got value: {}", value);
}
None => {
println!("Step 3: No value yet (as expected)");
println!(" - try_listen() returns None when nothing announced\n");
}
}
// STEP 4: Check if announced (alternative pattern)
// =================================================
// The announcement itself can check if a value was announced.
// This is useful in the sending/coordinating code.
if !announcement.is_announced() {
println!("Step 4: Confirmed - nothing announced yet\n");
}
// STEP 5: Announce the value
// ===========================
// This is the ONESHOT broadcast operation.
// - Stores the value in shared memory (Arc<OnceLock>)
// - Wakes up any waiting listeners (we don't have any waiting yet)
// - Can only be called ONCE (enforced by consuming the announcer)
//
// PERFORMANCE: ~10-40ns, completely non-blocking
println!("Step 5: Announcing value 42...");
announcer.announce(42).unwrap();
println!(" - Value 42 broadcasted to all listeners\n");
// NOTE: At this point, the announcer has been consumed.
// We can't announce again - the compiler prevents it!
// Try uncommenting this line to see the error:
// announcer.announce(99).unwrap(); // ERROR: value used after move
// STEP 6: Verify announcement happened
// =====================================
if announcement.is_announced() {
println!("Step 6: Confirmed - value has been announced\n");
}
// STEP 7: Retrieve the value
// ===========================
// Now try_listen() will return Some(value) immediately.
// The value is cloned from shared storage.
//
// IMPORTANT: For expensive-to-clone types, wrap in Arc<T> (see example 04).
match listener1.try_listen() {
Some(value) => {
println!("Step 7: Listener 1 received: {}", value);
println!(" - try_listen() now returns Some(42)\n");
}
None => {
println!("ERROR: This shouldn't happen!");
}
}
// STEP 8: Create additional listeners AFTER announcing
// =====================================================
// Listeners can be created even after the value is announced.
// They'll get the value immediately when they check.
//
// This is a KEY FEATURE - late joiners still get the value!
let listener2 = announcement.listener();
let listener3 = announcement.listener();
println!("Step 8: Created TWO more listeners AFTER announcing");
println!(" - These 'late joiners' still get the value\n");
// STEP 9: All listeners receive the same value
// =============================================
// Every listener, no matter when created, gets the same value.
// This is the "broadcast" part of oneshot broadcast.
println!("Step 9: All listeners can retrieve the value:");
println!(" - Listener 1: {:?}", listener1.try_listen());
println!(" - Listener 2: {:?}", listener2.try_listen());
println!(" - Listener 3: {:?}", listener3.try_listen());
println!("\n All received Some(42)!\n");
// STEP 10: Listeners can be called multiple times
// ================================================
// Calling try_listen() multiple times on the same listener
// returns the value each time (cloning it).
//
// TIP: This is safe but clones the value each time.
// For large types, use Arc<T> to avoid repeated cloning (see example 04).
println!("Step 10: Calling try_listen() again:");
println!(" - First call: {:?}", listener1.try_listen());
println!(" - Second call: {:?}", listener1.try_listen());
println!(" - Third call: {:?}", listener1.try_listen());
println!("\n Same value every time!\n");
// SUMMARY
// =======
println!("=== Summary ===");
println!("✓ Created announcement channel (announcer + announcement)");
println!("✓ Created listeners before AND after announcing");
println!("✓ Used try_listen() to check non-blocking");
println!("✓ Announced value ONCE (oneshot semantics)");
println!("✓ All listeners received the same value");
println!("\nNEXT: See example 02 for async listening with .await");
// EXPECTED OUTPUT:
// ================
// You should see all steps execute in order, showing:
// - try_listen() returns None before announcing
// - try_listen() returns Some(42) after announcing
// - All listeners get the value, even late joiners
// - Multiple calls to try_listen() return the same value
}