Trait PathExt

Source
pub trait PathExt: Path {
Show 14 methods // Provided methods fn borrow_opt(&self) -> Option<Ref<'_, <Self as Path>::Out>> { ... } fn borrow_opt_mut(&self) -> Option<BorrowMutGuard<'_, Self>> { ... } fn borrow_opt_mut_without_notifying( &self, ) -> Option<RefMut<'_, <Self as Path>::Out>> { ... } fn notify_changed(&self) { ... } fn get_opt(&self) -> Option<Self::Out> where Self::Out: Clone { ... } fn set_opt(&self, data: Self::Out) -> Result<(), Self::Out> where Self::Out: Sized { ... } fn until_change(&self) -> UntilChange<'_> { ... } fn signal_stream(&self, fire_immediately: bool) -> SignalStream<'_, Self> { ... } fn for_each<C: FnMut(&Self::Out)>(&self, closure: C) -> ForEach<'_, Self, C> { ... } fn for_each_async<F: Future<Output = ()>, C: FnMut(&Self::Out) -> F>( &self, closure: C, ) -> ForEachAsync<'_, Self, F, C> { ... } fn bind_for_each<C: FnMut(&Self::Out), I: Stream<Item = Self::Out>>( &self, on_change: C, incoming_changes: I, ) -> BindForEach<'_, Self, C, I> { ... } fn until_bubbling_change(&self) -> UntilChange<'_> { ... } fn build_path(self) -> <Self::Out as Trackable>::PathBuilder<Self> where Self: Sized, Self::Out: Trackable { ... } fn as_ref_path( &self, ) -> <Self::Out as Trackable>::PathBuilder<ReferencePath<'_, Self>> where Self::Out: Trackable { ... }
}
Expand description

An extension trait with methods for Path.

Most of the time, you’ll be working with methods from this trait (rather than the ones from Path).

This trait is implemented for every type that implements Path.

Provided Methods§

Source

fn borrow_opt(&self) -> Option<Ref<'_, <Self as Path>::Out>>

Borrow the data at this path immutably.

Returns None if the data identified by this path does not exist at the moment. For example…

  • The path might point to data in an enum variant, but the enum might be in a different variant.
  • The path might point to data associated with a specific key in a HashMap, but the HashMap might not have data at that key.

If there is an existing mutable borrow anywhere in the store, this method will panic.

See also: borrow - like this method, but for cases where we know None won’t be returned.

#[derive(Trackable)]
#[track(deep)]
enum MyEnum {
    Variant1(i32),
    Variant2(String)
}
let store = Store::new(MyEnum::Variant1(5));

let path_to_var1 = store.build_path().Variant1_0();
// Borrow the `i32` inside the first enum variant.
assert_eq!(path_to_var1.borrow_opt().as_deref(), Some(&5));

let path_to_var2 = store.build_path().Variant2_0();
// Can't borrow the `String`; the enum is in another variant.
assert!(path_to_var2.borrow_opt().is_none());
§Time Complexity

O(L) where L is the length of this path

Source

fn borrow_opt_mut(&self) -> Option<BorrowMutGuard<'_, Self>>

Borrow the data at this path mutably, notifying all the relevant change listeners when the returned borrow guard is dropped.

Returns None if the data identified by this path does not exist at the moment. For example…

  • The path might point to data in an enum variant, but the enum might be in a different variant.
  • The path might point to data associated with a specific key in a HashMap, but the HashMap might not have data at that key.

If there is an existing mutable or immutable borrow anywhere in the store, this method will panic.

See also: borrow_mut - like this method, but for cases where we know None won’t be returned.

#[derive(Trackable)]
#[track(deep)]
enum MyEnum {
    Variant1(i32),
    Variant2(String)
}
let store = Store::new(MyEnum::Variant1(5));

let path_to_var1 = store.build_path().Variant1_0();
// The enum is in the first variant so we are sure we can borrow
// and mutate the `i32`.
*path_to_var1.borrow_opt_mut().unwrap() += 1;
assert_eq!(path_to_var1.borrow_opt().as_deref(), Some(&6));

let path_to_var2 = store.build_path().Variant2_0();
assert!(path_to_var2.borrow_opt_mut().is_none());
§Time Complexity

O(L + D_1 + D_2 + … + D_N) where L is the length of this path and D_i is the length of the path of the ith subscriber (N is the number of subscribers that will be woken by the mutation, not the total number of subscribers in the store)

Source

fn borrow_opt_mut_without_notifying( &self, ) -> Option<RefMut<'_, <Self as Path>::Out>>

Borrow the data at this path mutably without notifying any listener.

Use this in conjunction with the notify_changed method.

Source

fn notify_changed(&self)

Notify all listeners that the data at this location has changed.

This method is not needed when using borrow_opt_mut or borrow_mut; in those cases notifications are sent automatically.

This method is useful in the relatively rare situations when you need borrow_opt_mut_without_notifying.

§Time Complexity

Same as borrow_opt_mut.

Source

fn get_opt(&self) -> Option<Self::Out>
where Self::Out: Clone,

Clone the data identified by this path.

Equivalent to path.borrow_opt().as_deref().map(Clone::clone).

Source

fn set_opt(&self, data: Self::Out) -> Result<(), Self::Out>
where Self::Out: Sized,

Set the data identified by this path, notifying listeners.

If the data cannot be accessed, Err is returned with the input.

Source

fn until_change(&self) -> UntilChange<'_>

Get a Stream that fires everytime a mutable borrow is taken of this or any encompassing piece of data.

In other words, whenever someone call borrow_opt_mut or borrow_mut on this path (the same one you’re calling until_change on) or any path that is a prefix of this one, the stream will fire.

#[derive(Default, Trackable)]
struct MyStruct {
    field_1: i32,
    field_2: u64
}
let store = Store::new(MyStruct::default());

// path to the `MyStruct` itself
let path = store.build_path();

let stream = path.until_change();

path.borrow_opt_mut(); // will fire the stream
path.field_1().borrow_opt_mut(); // will fire the stream
path.field_2().borrow_opt_mut(); // won't fire the stream
§Stream Behavior
§Spurious Fires

The stream may fire spuriously, although the chance of this happening is extremely low.

§Batching

If multiple changes happen in quick succession (“quick succession” means in-between calls to Stream::poll_next, to be precise), the stream may only fire once.

This means the stream can be used for detecting changes, but not for counting how many changes happened.

§Time Complexity

On creation and each poll: O(L) where L is the length of this path

Source

fn signal_stream(&self, fire_immediately: bool) -> SignalStream<'_, Self>

Get a Stream that fires once every time the data changes, yielding Refs to the data.

If the fire_immediately argument is true, then the stream will fire on the first poll too.

The stream ends when the data cannot be borrowed (when borrow_opt returns None).

This stream is useful for linking the data to some side-effect. For example, you can sync a UI widget content with the data…

use futures_lite::StreamExt;
// set the text with the current data
// and update the text whenever the data change
path.signal_stream(true).for_each(|data: Ref<'_, String>| {
    let s: &str = &**data;
    ui_element.set_text(s);
}).await;

Note that if this is all you need, use for_each instead.

§Panic Opportunity

This method returns a Ref. It is ‼️extremely important‼️ that you use the Ref and drop it as soon as possible. While the Ref is held, any attempt to mutably borrow any data in the store will panic. Holding the Ref for a long time (especially across a await points) is thus a very bad idea.

§Equivalent Behavior with Streams

This method is merely for convenience. You can achieve the same functionality yourself like so…

let stream = path.signal_stream(true);
// is equivalent to
let stream = futures_lite::stream::once(()) // fire immediately in the beginning...
    .chain(path.until_change()) // and after every change
    .map(|_| path.borrow_opt()) // borrow the data into a Ref
    .take_while(Option::is_some) // end the stream if the data is unavailable
    .map(Option::unwrap); // we've already checked that the data isn't none
Source

fn for_each<C: FnMut(&Self::Out)>(&self, closure: C) -> ForEach<'_, Self, C>

Execute the given function with the data as argument. Repeat every time the data changes.

The return future never finishes.

// set the text with the current data
// and update the text whenever the data change
path.for_each(|s: &String| {
    ui_element.set_text(s);
}).await;

This is a less powerful (and less panic-inducing) alternative to the signal_stream method.

Source

fn for_each_async<F: Future<Output = ()>, C: FnMut(&Self::Out) -> F>( &self, closure: C, ) -> ForEachAsync<'_, Self, F, C>

Execute the given async function (a function returning Future) with the data as argument. Repeat every time the data changes.

If the async function is still executing when the data changes, the execution is canceled (by dropping the Future) so that the new one can start.

The return future never finishes.

// when the URL changes, fetch the content and set the text
path_to_url.for_each_async(|url: &String| {
    let url = url.clone();
    async move {
        let content = fetch_content(&url).await;
        ui_element.set_text(&content);
    }
}).await;
Source

fn bind_for_each<C: FnMut(&Self::Out), I: Stream<Item = Self::Out>>( &self, on_change: C, incoming_changes: I, ) -> BindForEach<'_, Self, C, I>

For making a two-way binding.

The given on_change function is called once in the beginning and whenever the data changes (just like with for_each).

Whenever the given incoming_changes Stream yield a value, the data will be updated to that value. The on_change function won’t be called for changes that come in this way.

The future finishes when the incoming_changes stream yield None.

path.bind_for_each(
    // set the text with the current data
    // and update the text whenever the data change
    |s: &String| {
        text_field_widget.set_text(s);
    },
    // whenever the user type into the field, update the data
    text_field_widget
        .until_change()
        .map(|_| text_field_widget.get_text())
).await;
Source

fn until_bubbling_change(&self) -> UntilChange<'_>

Get a Stream that fires everytime a mutable borrow is taken of this piece of data or anything inside it.

In other words, whenever someone call borrow_opt_mut or borrow_mut on this path (the same one you’re calling until_bubbling_change on) or any path that this path is a prefix of, the stream will fire.

#[derive(Trackable)]
#[track(deep)]
struct MyStruct<T> {
    field_1: T,
    field_2: u64
}
let store = Store::new(MyStruct {
    field_1: MyStruct {
        field_1: String::new(),
        field_2: 123
    },
    field_2: 456
});

// path to the root `MyStruct` itself.
let root = store.build_path();

// path to the `field_1` in the root `MyStruct`.
let listening = root.field_1();
let stream = listening.until_bubbling_change();

root.field_1().borrow_opt_mut(); // will fire the stream
root.field_1().field_1().borrow_opt_mut(); // will fire the stream
root.field_1().field_2().borrow_opt_mut(); // will fire the stream
root.borrow_opt_mut(); // won't fire the stream
root.field_2().borrow_opt_mut(); // won't fire the stream
§Stream Behavior

The returned stream may spuriously fire or batch changes. See until_change documentation.

§Time Complexity

On creation: O(L) where L is the length of this path

On each poll: O(1)

Source

fn build_path(self) -> <Self::Out as Trackable>::PathBuilder<Self>
where Self: Sized, Self::Out: Trackable,

Gives a PathBuilder for creating a path that continues from this path.

This is useful when you are handling the type that implements Path directly. Most of the time, though, you will already be working with PathBuilders.

fn modify_string(path: impl Path<Out = String>) {
    path.set_opt("haha".to_string()).ok();
}
#[derive(Trackable)]
struct MyStruct {
    s: String
}
let store = Store::new(MyStruct {
    s: String::from("hello world")
});
modify_string(store.build_path().s().into_path());
assert_eq!(&**store.build_path().s().borrow(), "haha");
Source

fn as_ref_path( &self, ) -> <Self::Out as Trackable>::PathBuilder<ReferencePath<'_, Self>>
where Self::Out: Trackable,

Create a new Path from borrowing this path.

let store = Store::new(MyStruct::default());

// `path_a` lives for as long as the store.
let path_a = store.build_path();

// `path_b` composes `path_a`.
// `path_b` lives for as long as the store.
let path_b = path_a.field_1();

// `path_c` borrows `path_a`.
// if you drop `path_a`, `path_c` will no longer be usable.
let path_c = path_a.as_ref_path().field_1();

Note that the new path will only live for as long as the borrow. This means it won’t be useful for use cases requiring long-lived path (such as implementing an undo-redo log).

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<P: Path + ?Sized> PathExt for P

Extension trait is blanket-implemented.