不使用std将*mut u8转换为&[u8]的方法

7

我正在编写用于处理来自JavaScript环境中字符串的WebAssembly Rust代码。

由于WebAssembly没有真正的字符串类型,因此我试图传递指向UTF-8编码字符串的WebAssembly内存对象的指针。

#[no_mangle]
pub extern "C" fn check(ptr: *mut u8, length: u32) -> u32 {
    unsafe {
        let buf: &[u8] = std::slice::from_raw_parts(ptr, length as usize);
        // do some operations on buf
        0
    }
}

它很好用,只是我不得不依赖于std库,这使最终的二进制文件膨胀到约600KB。

有没有办法摆脱std::slice::from_raw_parts,但仍能将原始指针转换为切片?


“which bloats the final binary” — 这听起来像是工具链的问题。你是否在运行 wasm-gc? - Shepmaster
1
@Shepmaster wasm-gc非常有用。它可以将二进制文件缩小到约248KB。但是如果没有std crate,二进制文件的大小可以缩小到18KB,其中包含约17KB的数据和仅约1KB的代码。 - liuyanghejerry
问题在于,您从libcorelibstd使用的代码量是完全相同的。 std::slice::from_raw_parts_mut就是core::slice::from_raw_parts_mut。从一个库切换到另一个库不应该改变任何东西。这似乎确实像某些未使用的代码或数据没有被正确删除的情况。 - Shepmaster
事实上,为了摆脱 std,我还添加了 "#![no_std]" 及其对应项。这些代码对最终的二进制文件产生了很大的影响。 - liuyanghejerry
@Shepmaster 我明白你的意思。但我不知道为什么会发生这种情况。我为这个问题制作了一个最简单的示例,以防您想要重现它:https://github.com/liuyanghejerry/rust-wasm-size-bug - liuyanghejerry
显示剩余3条评论
2个回答

8
在Rust中,您无法将裸指针强制转换为切片,因为切片不仅仅是指针,它还包括大小(否则它就不能保证安全)。
如果您不想使用std,可以使用core crate
extern crate core;

#[no_mangle]
pub extern "C" fn check(ptr: *mut u8, length: u32) -> u32 {
    unsafe {
        let buf: &mut [u8] = core::slice::from_raw_parts_mut(ptr, length as usize);
    }
    // do some operations on buf
    0
}

core cratestd crate的一部分,适用于嵌入式系统,即没有需要分配的所有内容。


7
可以手动构建类似于切片的东西,它是一个由细指针和长度组成的fat pointer。然后将一个指向此结构的指针强制转换为指向切片的指针。
这种方法不仅不安全,而且依赖 Rust 的内部实现(切片的内存布局),这不能保证在编译器版本或甚至系统之间稳定。如果您想确保代码将来能正确运行,@Boiethios 的答案是可行的。然而,出于教育目的,下面的代码可能仍然很有趣:
unsafe fn make_slice<'a>(ptr: *const u8, len: usize) -> &'a [u8] {
    // place pointer address and length in contiguous memory
    let x: [usize; 2] = [ptr as usize, len];

    // cast pointer to array as pointer to slice
    let slice_ptr = &x as * const _ as *const &[u8];

    // dereference pointer to slice, so we get a slice
    *slice_ptr
}

fn main() {
    let src: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
    let raw_ptr = &src[1] as *const u8;

    unsafe {
        println!("{:?}", make_slice(raw_ptr, 3));  // [2, 3, 4]
    }
}

(在Rust稳定版1.26.2上测试, playground

1
这非常有趣。我能否追踪到此方法的任何文档或来源? - liuyanghejerry
1
@liuyanghejerry 切片的内存布局在这本书(图4-6)中有简要描述。了解这一点后,剩下的就是指针操作,重新解释内存中的内容含义。 - MB-F

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接