ccbrown / iocraft

A Rust crate for beautiful, artisanally crafted CLIs, TUIs, and text-based IO.
https://crates.io/crates/iocraft
Apache License 2.0
320 stars 4 forks source link

Requirement for `Copy` trait on `State ` makes deserialization tricky #31

Open anussel5559 opened 5 hours ago

anussel5559 commented 5 hours ago

Qualification: I'm still fairly new to rust and lifetimes in general, so I could be overlooking something simple.

Scenario: I have a configuration yaml that I use serde to deserialize. The deserialized struct somewhat easily has String properties, but that Struct cannot be used in any State within iocraft since it can't easily derive the Copy trait (well because String and Copy are at odds).

I managed to deserialize in to an intermediate struct (using String properties) and secondarily implement From to convert to the final struct with appropriate &str properties to satisfy a derived Copy trait for the final struct.

This feels like a fairly big hoop for what I'd expect to be a common use-case of iocraft, so I'm guessing I'm missing something obvious here and would love to understand better what shortcuts/patterns might exist to not have to jump through this hoop!

ccbrown commented 4 hours ago

Thanks for opening the issue for this. I think this partly is a misunderstanding, but maybe there is something that can be improved in the API and/or documentation as well.

State doesn't actually have a Copy requirement. Here's a minimal example with a non-copyable state:

use iocraft::prelude::*;
use std::time::Duration;

#[derive(Default)]
struct MyState {
    s: String,
}

#[component]
fn Example(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
    let mut my_state = hooks.use_state(|| MyState::default());

    hooks.use_future(async move {
        loop {
            smol::Timer::after(Duration::from_millis(100)).await;
            let new_state = MyState {
                s: my_state.read().s.clone() + "a",
            };
            my_state.set(new_state);
        }
    });

    element! {
        Text(color: Color::Blue, content: &my_state.read().s)
    }
}

fn main() {
    smol::block_on(element!(Example).render_loop()).unwrap();
}

The weather example also has a lot of non-copyable state.

There are some functions defined on State that do require Copy though. I would guess you might be trying to use state.get(), which is one of them. If you have a non-copyable state type, you would have to use .read() instead as shown above.

Does that clear things up? If not, feel free to put together a code sample that demonstrates the issue and I'll see what else I can do to help!