maciejhirsz / kobold

Easy declarative web interfaces.
https://docs.rs/kobold/
Mozilla Public License 2.0
385 stars 7 forks source link

Unsure how to dereference multiple fields of a struct using `Deref` and `DerefMut` #52

Closed ltfschoen closed 1 year ago

ltfschoen commented 1 year ago

I'm trying to make this Invoice example, here is the latest.

It uses code from csv_editor and todomvc. In the csv_editor example Table is used to store a table of rows and columns in the State after the data is loaded from a CSV file no. 1 and it uses Deref and DerefMut here for Table

In the Invoice example, i added another struct TableFileDetails here that stores a separate table of rows and columns in the State after the data is loaded from a different CSV file no. 2

But I don't know how to implement Deref and DerefMut for both Table and TableFileDetails that are both used by State?

In the DerefMut docs they only give this example https://doc.rust-lang.org/std/ops/trait.DerefMut.html#examples of a "struct with a single field which is modifiable by dereferencing the struct".

If I follow along with trying to use their example I end up with:

pub struct State<U, V> {
    ...
    pub table: U,
    pub table_file_details: V,
    ...
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Table {
    pub source: TextSource,
    pub columns: Vec<Text>,
    pub rows: Vec<Vec<Text>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct TableFileDetails {
    pub source: TextSource,
    pub columns: Vec<Text>,
    pub rows: Vec<Vec<Text>>,
}

impl<T, U> Deref for State<T, U> {
    type Target = T;

    fn deref(&self) -> &mut Self::Target {
        &self.table
    }
}

impl<T, U> DerefMut for State<T, U> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.table
    }
}

impl<T, U> Deref for State<T, U> {
    type Target = U;

    fn deref(&self) -> &mut Self::Target {
        &self.table_file_details
    }
}

impl<T, U> DerefMut for State<T, U> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.table_file_details
    }
}

But then I get a lot of errors that I try to resolve, but the main one is the following:

error[E0119]: conflicting implementations of trait `Deref` for type `state::State<_, _>`
   --> examples/invoice/src/state.rs:204:1
    |
190 | impl<T, U> Deref for State<T, U> {
    | -------------------------------- first implementation here
...
204 | impl<T, U> Deref for State<T, U> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `state::State<_, _>`

error[E0119]: conflicting implementations of trait `DerefMut` for type `state::State<_, _>`
   --> examples/invoice/src/state.rs:212:1
    |
198 | impl<T, U> DerefMut for State<T, U> {
    | ----------------------------------- first implementation here
...
212 | impl<T, U> DerefMut for State<T, U> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `state::State<_, _>`

And I can't see how to to return multiple targets from deref and deref_mut

maciejhirsz commented 1 year ago

Can't do that. You can chain derefs so that if &T derefs into &U, which derefs into &V, then you can call methods from both U and V on T, but you can never have more than one Deref target for any given type.

If you need multiples you need to implement AsRef<T> of which you can have multiple. In this case though you should just access table_file_details (maybe just call it file_details?) by field name.