Is there a way to simplify the access to an inner functionality of web_sys?

Viewed 157

After reading the Rust book, I've decided to give it a try with Web Assembly. I'm creating a simple tracker script to practice and learn more about it. There are a couple of methods that need to access the window, navigator or cookie API. Every time I have to access any of those there are a lot of boiler plate code involved:

pub fn start() {
        let window = web_sys::window().unwrap();
        let document = window.document().unwrap();
        let html = document.dyn_into::<web_sys::HtmlDocument>().unwrap();
        let cookie = html_document.cookie().unwrap();
}

That's unpractical and bothers me. Is there a smart way to solve this? I've in fact tried to use lazy_static to have all of this in a global.rs file:

#[macro_use]
extern crate lazy_static;

use web_sys::*;

lazy_static! {
    static ref WINDOW: window = {
        web_sys::window().unwrap()
    };
}

But the compile fails with: *mut u8 cannot be shared between threads safely`.

2 Answers

You could use the ? operator instead of unwrapping.

Instead of writing

pub fn start() {
  let window = web_sys::window().unwrap();
  let document = window.document().unwrap();
  let html = document.dyn_into::<web_sys::HtmlDocument>().unwrap();
  let cookie = html_document.cookie().unwrap();
}

You can write

pub fn start() -> Result<()> {
  let cookie = web_sys::window()?
                 .document()?
                 .dyn_into<web_sys::HtmlDocument>()?
                 .cookie()?;
  Ok(())
}

It's the same number of lines, but less boilerplate and for simpler cases a one-liner.

If you really don't want to return a result you can wrap the whole thing in a lambda, (or a try block if you're happy using unstable features).

pub fn start() {
  let cookie = (|| Result<Cookie)> {
    web_sys::window()?
      .document()?
      .dyn_into<web_sys::HtmlDocument>()?
      .cookie()
   }).unwrap();
}

if you find you don't like repeating this frequently - you can use functions

fn document() -> Result<Document> {
  web_sys::window()?.document()
}

fn html() -> Result<web_sys::HtmlDocument> {
  document()?.dyn_into<web_sys::HtmlDocument>()
}

fn cookie() -> Result<Cookie> {
  html()?.cookie()
}

pub fn start() {
  let cookie = cookie()?;
}

That's unpractical and bothers me.

Unsure what's your issue here, but if you access the same cookie again and again in your application, perhaps you can save it in a struct and just use that struct? In my recent WebAssembly project I saved some of the elements I've used in a struct and used them by passing it around.

I also think that perhaps explaining your specific use case might lead to more specific answers :)

Related