0

I have a simple Yew app in which I draw a binary tree and render it in HTML. I want to make it so that I save in the model (the App type) which Node or Leaf is currently selected. (I will then color that one differently)

I added an Annot struct, and added that as a field in both Node and Leaf. Now, I have a SelectTree variant in the Msg type. I want to add an argument to this, something like SelectTree(&Tree) but this gives me an "expected named lifetime parameter" error.

I checked the examples but they all had data representable in arrays/vectors so passing an index was enough, so I didn't learn much from them. What can I do to pass a reference in the message? Is this even possible? If it is not possible, can I do this using unsafe, and how?

Here is the code I have right now:

use yew::{html, Component, Context, Html};
use gloo::console::{self};

#[derive(Debug, Clone)]
pub struct Annot {
    is_selected: bool
}

impl Default for Annot {
    fn default() -> Annot { Annot { is_selected: false } }
}

#[derive(Debug, Clone)]
pub enum Tree {
    Leaf { ann: Annot },
    Node { ann: Annot, x: usize, l: Box<Tree>, r: Box<Tree> },
}

impl Tree {
    pub fn new() -> Tree {
        Tree::Leaf { ann: Default::default() } 
    }

    pub fn insert(&mut self, v : usize) -> () {
        match self {
            Tree::Leaf {ann} => {
                *self = Tree::Node { 
                    ann: Default::default(), 
                    x: v,
                    l: Box::new(Tree::Leaf { ann: Default::default() }),
                    r: Box::new(Tree::Leaf { ann: Default::default() })
                };
            }
            Tree::Node {ann, x, l, r} => {
                if v < *x { l.insert(v); } else if *x < v { r.insert(v); }
            }
        }
    }


    pub fn view(&self, ctx: &Context<App>) -> Html {
        match self {
            Tree::Leaf {ann} => {
                let cls = if ann.is_selected { "selected" } else { "" };
                html! { 
                    <li> 
                        <a class={cls} 
                            onclick={ctx.link().callback(|_| Msg::SelectTree)}>
                        </a> 
                    </li> 
                }
            }
            Tree::Node {ann, x, l, r} => {
                let cls = if ann.is_selected { "selected" } else { "" };
                html! {
                    <li>
                        <a class={cls} 
                            onclick={ctx.link().callback(|_| Msg::SelectTree)}>
                            { x }
                        </a>
                        <ul>
                            { l.view(ctx) }
                            { r.view(ctx) }
                        </ul>
                    </li>
                }
            }
        }
    }
}

pub enum Msg {
    SelectTree
}

pub struct App {
    tree: Tree
}

impl Component for App {
    type Message = Msg;
    type Properties = ();

    fn create(ctx: &Context<Self>) -> Self {
        let mut t = Tree::new();
        for i in [5,3,7,4,2,6,8] { t.insert(i); }
        Self { tree: t }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::SelectTree => {
                console::info!("Selected!");
                true
            }
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        html! { 
            <div class="tree"> 
            <ul> { self.tree.view(ctx) } </ul> 
            </div> 
        }
    }
}

fn main() {
    yew::start_app::<App>();
}

PS: I come from a JavaScript and functional programming background, and I'm relatively a beginner at Rust. I've been trying to solve this for a few days already so I think I need things to be explained more explicitly.

Joomy Korkut
  • 514
  • 1
  • 5
  • 15
  • I have 3 recommendations for you: 1. Read the full error messages from cargo/rustc, not only what your IDE gives you. rustc will flat out tell you (press Build) to use pub enum Msg<'a> { SelectTree(&'a Tree) 2. This is pretty basic stuff. Read on in your Rust book a bit more. 3. If you're this new to rust, avoid lifetimes in structs and return types. Use Arcs, or just clone where necessary. – Caesar Jun 06 '22 at 14:12
  • Adding a lifetime parameter to the Msg type is not an option, Yew doesn't allow the message type to have a lifetime parameter. – Joomy Korkut Jun 06 '22 at 17:55

1 Answers1

0

Sending Rc<RefCell<ATree>> in the message and also storing the same type in the model works. The only difference is that you have to use callback_once instead of callback, since callback_once also accepts callbacks of trait FnOnce.

Joomy Korkut
  • 514
  • 1
  • 5
  • 15