• [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> 
    
    • 1
    • 2

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

    #[derive(Properties, Clone, PartialEq)]
    pub struct Props {
        ...
        #[prop_or(200)]
        pub width: u32,
        #[prop_or(500)]
        pub height: u32,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    也就是说,我要将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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    逻辑比较简单,步骤如下

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    也就是说,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;
    
    • 1
    • 2

    报错:

    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
    
    • 1
    • 2

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

    fn main() {
        let num: f64 = -323.3;
        let num1: u32 = num as u32;
    
        println!("numbers:{}, {}", num, num1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打印结果如下:

    numbers:-323.3, 0
    
    • 1

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

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

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

    numbers:-3, 4294967293 
    
    • 1

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

    正确的做法是:

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

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

    总结

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

    今天就到这里,欢迎大家留言交流。

  • 相关阅读:
    Day26-龟兔赛跑案例、Callable接口、静态代理、Lambda表达式
    35.Python面向对象(八)【元类:type()、__metaclass__属性、实现简易ORM框架】
    Vue 中 KeepAlive 内置缓存使用
    【小程序】集成echarts问题记录
    Laravel5使用box/spout扩展,大文件导出CSV文件
    解决 Android 分享到小程序 封面显示不全
    USB应用实战视频教程第3期:手把手玩转USB BULK方式下位机和QT6.4上位机开发(上篇)
    java游戏制作-拼图游戏
    Socks5代理IP在网络安全、跨境电商和游戏中的应用
    DirectXShaderCompiler mac编译
  • 原文地址:https://blog.csdn.net/firefox1/article/details/133320578