[2023.09.26]: JsValue的转换体验与as关键字的浅析

昨天解决了焦点问题,今天就开始搬砖了。本以为可以一帆风顺,但是还是遇到了几个问题,不过还好,都被一一解决,这里我分享一下JsValue的转换体验以及关键字as的使用浅析。

场景描述

我是在什么情况下遇到JsValue的转换的呢?获取当前窗口的内部宽度和内部高度。api的接口定义如下:

pub fn inner_width(&self) -> Result<JsValue, JsValue>
pub fn inner_height(&self) -> Result<JsValue, JsValue> 

我使用当前窗口的内部宽度和内部高度的地方在Modal组件中:

#[derive(Properties, Clone, PartialEq)]
pub struct Props {
    ...
    #[prop_or(200)]
    pub width: u32,
    #[prop_or(500)]
    pub height: u32,
}

也就是说,我要将JsValue转换成u32类型。

问题解决

先看这个问题最终是怎么解决的吧。和Javascript代码比起来,是稍微复杂了一点点。

pub fn get_window_size() -> Option<(u32, u32)> {
    if let Some(window) = get_window() {
        let width = window
            .inner_width()
            .unwrap_or_else(|_| JsValue::from_f64(default_width1));
        let height = window
            .inner_height()
            .unwrap_or_else(|_| JsValue::from_f64(default_height1));
            
        let width1: f64 = width.as_f64()?;
        let height1: f64 = height.as_f64()?;
        
        let width2 = width1 as u32;
        let height2 = height1 as u32;
        
        return Some((width2, height2));
    }
    None
}

逻辑比较简单,步骤如下

  1. 先通过window对象拿到widthheight;
  2. 通过方法as_f64将JsValue转换成f64类型;
  3. 通过rust的as关键字将f64类型转换成u32类型;

问题展开

从问题的解决层面上来说,明确上面3步,就可以拿到我们需要的数据,但是这显然只是冰山一角。水下面的东西很多,这里我挑2个容易的来说。

关于JsValue的数据转换

JsValue定义在wasm_bindgen中,关于数据转换的接口有下面这几个:

pub fn as_bool(&self) -> Option<bool>;
pub fn as_f64(&self) -> Option<f64>;
pub fn as_string(&self) -> Option<String>;

pub const fn from_bool(b: bool) -> JsValue;
pub fn from_f64(n: f64) -> JsValue;
pub fn from_str(s: &str) -> JsValue;

也就是说,wasm_bindgen::JsValue中,只关心布尔值,字符串和数字,而数字在rust中使用的是f64,即rust std中支持的容量最大,精度最高的数字。JsValue代表了Javascript环境中的数据。因此,了解这一点,我们就可以自由的进行Javascript和Rust的数据交换了。

关于as

在上面的代码中,我们通过rust的as关键字,将f64数字类型转换成了u32数字类型。
在Rust中,as关键字可以用于类型转换,将一个类型转换为另一个类型,这里的类型必须原始类型(primitive types),但显然不是指所有的原始类型。
什么是原始类型呢?原始类型是Rust语言的基本的数据类型,它们是语言的一部分。Rust语言的原始类型包括一下几种:

布尔类型(bool):表示逻辑值,只能是true或false。
字符类型(char):表示单个Unicode字符。
整数类型(integer):包括有符号整数(i8、i16、i32、i64、isize)和无符号整数(u8、u16、u32、u64、usize)。
浮点数类型(floating-point):包括单精度浮点数(f32)和双精度浮点数(f64)。
元组类型(tuple):可以包含多个不同类型的值。
数组类型(array):包含固定长度的相同类型的值。
切片类型(slice):对数组的引用,可以动态指定长度。
指针类型(pointer):包括原始指针(const T和mut T)和引用(&T)。

在我们的问题场景中,我们用asf64转换成了u32。难道还能将tuple转换成u32不成,显然是不可能的。还好编译器会给出提示。

    let num1 = (-33, -5);
    let num: u32 = num1 as u32;

报错:

3 |     let num: u32 = num1 as u32;
  |                    ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

其实在这里,我用as关键字是迫不得已的。因为我没有在u32这个类型上找到impl TryFrom<f64> for u32这样的方法。那将f64类型的数据转换成u32类型的数据,编译器没有报错,是不是就意味着没有问题呢?在我们的问题场景中,应该是没有问题的。但是从数据本身的角度,是有问题的。

fn main() {
    let num: f64 = -323.3;
    let num1: u32 = num as u32;

    println!("numbers:{}, {}", num, num1);
}

打印结果如下:

numbers:-323.3, 0

我想你应该总结处理这里的规律了吧,再看看下面这段就让你大跌眼镜了:

fn main() {
    let num: i32 = -3;
    let num1: u32 = num as u32;
    println!("numbers:{}, {}", num, num1);
}

按照之前的f64u32的经验,打印的结果应该是:-3, 0,对吧?
错,结果也让我吓一跳:

numbers:-3, 4294967293 

u32的最大数值是:4294967295。
这里的水有多深,我就不往下说了,我怕淹死在这里,哈哈。

正确的做法是:

fn main() {
    let num: i32 = -3;
    let num1: u32 = u32::try_from(num).unwrap_or_else(|_| 0);
    println!("numbers:{}, {}", num, num1);
}

所以,关于使用as来转换数据,如果类型本身实现了TryFrom<T> for ...,那么就不要使用as来进行类型转换。

总结

在基于wasm_bindgen的WebAssembly开发过程中,JsValue与Rust原始数据类型之间的转换会是家常便饭。对于JsValue来说,它只关心布尔值,字符串和数字,分别对应Rust中的boolStringf64f64与其它Rust数字之间的转换需要依赖as关键字。但是,如果数字类型实现了TryFrom<T>这个trait,就不要使用as关键字来进行转换。

今天就到这里,欢迎大家留言交流。文章来源地址https://uudwc.com/A/qREwM

原文地址:https://blog.csdn.net/firefox1/article/details/133320578

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

上一篇 2023年09月28日 14:04
Flask框架【before_first_request和before_request详解、钩子函数、Flask_信号机制】(七)
下一篇 2023年09月28日 15:04