Dear Community . I am a new Rust user, and I am just working my way into the Rust world. So far, I have 20 years of C# experience. As my first Rust project, I'm working on a simple desktop tool, which I'm developing with Tauri and Leptos (WASM, https://www.leptos.dev/).
My first Rust steps were very enjoyable, so I fell in love with Rust right away . However, I'm now stuck at a point where I can't get anywhere without you guys.
It started when I saw the video "Use Arc Instead of Vec" by Logan Smith (Use Arc Instead of Vec - YouTube). In the video, Logan explains that str
works just as well instead of Vec
. Well, I just had a string in my Rust program and wanted to try his hint just for practice.
Basically, it worked well and I came to the following result. This is a GUI component in Leptos - so in the frontend with WASM. I implemented Logan's idea here for the file path path_arc
(which comes from the backend) and for its default value no file selected
:
use std::sync::{Arc, OnceLock};
use leptos::*;
use wasm_bindgen::prelude::*;
use crate::components::{button::*};
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "tauri"])]
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}
#[component]
pub fn Welcome(cx: Scope) -> impl IntoView {
let default_text = "no file selected".to_string();
let default_value: Arc<str> = Arc::from(default_text);
let (chosen_file, set_chosen_file) = create_signal(cx, default_value);
let import = move || {
spawn_local(async move {
let import_file_path_temp = invoke("import_file", JsValue::UNDEFINED).await;
if let Some(import_file_path) = import_file_path_temp.as_string() {
let path_arc: Arc<str> = Arc::from(import_file_path);
set_chosen_file.set(path_arc);
}
});
};
view! { cx,
<div class="d-flex flex-column justify-content-center align-items-center" style="height: 80vh;">
<img src="public/icons/splashscreen.png" alt="Main Icon" width="128" height="128"/>
<h1>"Welcome"</h1>
<div class="mt-5">
<ImageButton icon_name="import" text="Import" on_click=import />
<ImageButton icon_name="export" text="Export" on_click=|| {} />
</div>
<div>
"Debug ... chosen file: " {move || chosen_file.get().to_string()}
</div>
</div>
}
}
Coming, as I said, from the C# world, I thought: there I would define the default value once e.g. in the constructor for a static readonly
field or ideally as const
field. That's how I learned about OnceLock
and OnceCell
. I have tried to implement this:
use std::sync::{Arc, OnceLock};
use leptos::*;
use wasm_bindgen::prelude::*;
use crate::components::{button::*};
fn default_value<'a>() -> &'a Arc<str> {
static DEFAULT_VALUE: OnceLock<Arc<str>> = OnceLock::new();
DEFAULT_VALUE.get_or_init(|| Arc::from("no file selected"))
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "tauri"])]
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}
#[component]
pub fn Welcome(cx: Scope) -> impl IntoView {
let (chosen_file, set_chosen_file) = create_signal(cx, default_value());
let import = move || {
spawn_local(async move {
let import_file_path_temp = invoke("import_file", JsValue::UNDEFINED).await;
if let Some(import_file_path) = import_file_path_temp.as_string() {
let path_arc: Arc<str> = Arc::from(import_file_path);
set_chosen_file.set(&path_arc);
}
});
};
view! { cx,
<div class="d-flex flex-column justify-content-center align-items-center" style="height: 80vh;">
<img src="public/icons/splashscreen.png" alt="Main Icon" width="128" height="128"/>
<h1>"Welcome"</h1>
<div class="mt-5">
<ImageButton icon_name="import" text="Import" on_click=import />
<ImageButton icon_name="export" text="Export" on_click=|| {} />
</div>
<div>
"Debug ... chosen file: " {move || chosen_file.get().to_string()}
</div>
</div>
}
}
However, this does not work: Instead of a Arc<str>
, I now have a &Arc<str>
, which seems logical to me at first. After all, I don't want a clone to be created but the default value to be reused. It also works so far, except for the assignment of the path that comes from the backend set_chosen_file.set(&path_arc);
. I get the error 'path_arc' does not live long enough [E0597] borrowed value does not live long enough
.
I read in the Rust book, of course. After reading chapter 4 "Understanding Ownership", I thought I can somehow move the ownership, away from the current local scope into the signal; so that my variable path_arc
cannot be used in the scope afterward. But this is precisely where I am currently stuck. Somehow I haven't quite understood the concept yet...
Can anyone help me with this ? The whole thing is just for practice, because, of course, I could have all the code running either without
Arc<str>
or at least without OnceLock
.
Best
Thorsten