# Observable Property
A thread-safe observable property implementation for Rust that allows you to observe changes to values across multiple threads.
## Features
* **Thread-safe**: Uses `Arc<RwLock<>>` for safe concurrent access
* **Observer pattern**: Subscribe to property changes with callbacks
* **Filtered observers**: Only notify when specific conditions are met
* **Async notifications**: Non-blocking observer notifications with background threads
* **Panic isolation**: Observer panics don't crash the system
* **Type-safe**: Generic implementation works with any `Clone + Send + Sync + 'static` type
* **Zero dependencies**: Uses only Rust standard library
## Quick Start
Add this to your `Cargo.toml`:
```toml
[dependencies]
observable-property = "0.1.0"
```
## Usage
### Basic Example
```rust
use observable_property::ObservableProperty;
use std::sync::Arc;
// Create an observable property
let property = ObservableProperty::new(42);
// Subscribe to changes
let observer_id = property.subscribe(Arc::new(|old_value, new_value| {
println!("Value changed from {} to {}", old_value, new_value);
})).unwrap();
// Change the value (triggers observer)
property.set(100).unwrap(); // Prints: Value changed from 42 to 100
// Unsubscribe when done
property.unsubscribe(observer_id).unwrap();
```
### Multi-threading Example
```rust
use observable_property::ObservableProperty;
use std::sync::Arc;
use std::thread;
let property = Arc::new(ObservableProperty::new(0));
let property_clone = property.clone();
// Subscribe from one thread
})).unwrap();
// Modify from another thread
}).join().unwrap();
```
### Filtered Observers
```rust
use observable_property::ObservableProperty;
use std::sync::Arc;
let counter = ObservableProperty::new(0);
// Only notify when value increases
let observer_id = counter.subscribe_filtered(
Arc::new(|old, new| println!("Increased: {} -> {}", old, new)),
|old, new| new > old
).unwrap();
counter.set(5).unwrap(); // Triggers observer: "Increased: 0 -> 5"
counter.set(3).unwrap(); // Does NOT trigger observer
counter.set(10).unwrap(); // Triggers observer: "Increased: 3 -> 10"
```
### Async Notifications
For observers that might perform time-consuming operations, use async notifications to avoid blocking:
```rust
use observable_property::ObservableProperty;
use std::sync::Arc;
use std::time::Duration;
let property = ObservableProperty::new(0);
std::thread::sleep(Duration::from_millis(100));
println!("Slow observer: {} -> {}", old, new);
})).unwrap();
// This returns immediately even though observer is slow
property.set_async(42).unwrap();
```
### Complex Types
Observable properties work with any type that implements the required traits:
```rust
use observable_property::ObservableProperty;
use std::sync::Arc;
#[derive(Clone, Debug)]
struct Person {
name: String,
age: u32,
}
let person_property = ObservableProperty::new(Person {
name: "Alice".to_string(),
age: 30,
});
person_property.subscribe(Arc::new(|old_person, new_person| {
println!("Person changed: {:?} -> {:?}", old_person, new_person);
})).unwrap();
person_property.set(Person {
name: "Alice".to_string(),
age: 31,
}).unwrap();
```
## Error Handling
All operations return `Result` types with descriptive errors:
```rust
use observable_property::{ObservableProperty, PropertyError};
let property = ObservableProperty::new(42);
match property.get() {
Ok(value) => println!("Current value: {}", value),
Err(PropertyError::PoisonedLock) => println!("Lock was poisoned!"),
Err(e) => println!("Other error: {}", e),
}
```
## Performance Considerations
* **Read operations** are very fast and can be performed concurrently from multiple threads
* **Write operations** are serialized but optimize for quick lock release
* **Synchronous notifications** block the setter until all observers complete
* **Asynchronous notifications** return immediately and run observers in background threads
* **Observer panics** are isolated and won't affect other observers or crash the system
## Examples
Run the included examples to see more usage patterns:
```bash
# Basic usage example
cargo run --example basic
# Multithreaded usage with performance comparisons
cargo run --example multithreaded
```
## Safety
This crate is designed with safety as a primary concern:
* Thread-safe access patterns prevent data races
* Observer panics are caught and isolated
* Lock poisoning is properly handled and reported
* No unsafe code is used
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](https://www.google.com/search?q=LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license ([LICENSE-MIT](https://www.google.com/search?q=LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.