You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Apache Arrow从Node.js无拷贝传数据到Rust?遇Napi-rs报错

使用Apache Arrow在Node.js与Rust间传递数据的解决方案

问题概述

使用Apache Arrow格式从Node.js向Rust传递数据时,共享内存环节出现异常:

  • 直接传递Arrow Vector对象给Rust函数时,触发Failed to create reference from Buffer错误
  • 尝试传递arrowVector.data[0].buffers时,触发断言错误../src/node_buffer.cc:245:char *node::Buffer::Data(Local<v8::Value>): Assertion val->IsArrayBufferView()' failed.`

核心问题原因

  1. napi-rs的Buffer类型仅接受Node.js标准Buffer或符合ArrayBufferView规范的类型(如Uint8Array),而Arrow Vector的内部缓冲区是自定义结构,无法直接匹配
  2. arrowVector.data[0].buffers是多缓冲区数组,不能直接作为单个Buffer参数传入Rust函数

正确实现方案

方案1:手动提取Arrow缓冲区(适合轻量场景)

Node.js端代码

import { makeVector } from 'apache-arrow';
import { testFn } from './index.js';

// 创建Arrow Vector
const LENGTH = 2000;
const rainAmounts = Float32Array.from(
    { length: LENGTH },
    () => Number((Math.random() * 20).toFixed(1))
);

const arrowVector = makeVector(rainAmounts);

// 提取所有缓冲区并转换为Uint8Array(符合ArrayBufferView规范)
const buffers = arrowVector.data
  .map(fieldData => fieldData.buffers.map(buf => new Uint8Array(buf)))
  .flat();

// 传递缓冲区数组到Rust
testFn(buffers);

Rust端代码

use napi::bindgen_prelude::Buffer;
use bytemuck::cast_slice;

#[napi]
pub fn test_fn(buffers: Vec<Buffer>) {
    println!("收到{}个缓冲区", buffers.len());
    
    // 处理浮点值缓冲区(Arrow Vector的第二个缓冲区通常是值数据)
    if let Some(value_buf) = buffers.get(1) {
        let float_values: &[f32] = cast_slice(value_buf.as_ref());
        println!("第一个浮点值:{}", float_values[0]);
    }
}

需在Cargo.toml中添加依赖:bytemuck = "1.13",用于安全转换字节切片为数值类型

方案2:序列化Arrow IPC格式(推荐,兼容完整Arrow特性)

直接将Arrow Vector序列化为标准IPC二进制格式,在Rust端用官方Arrow库解析,避免手动处理缓冲区的复杂性。

Node.js端代码

import { makeVector } from 'apache-arrow';
import { testFn } from './index.js';

// 创建Arrow Vector
const LENGTH = 2000;
const rainAmounts = Float32Array.from(
    { length: LENGTH },
    () => Number((Math.random() * 20).toFixed(1))
);

const arrowVector = makeVector(rainAmounts);

// 序列化为Arrow IPC二进制数据
const ipcBuffer = arrowVector.toArray().buffer;

// 传递IPC缓冲区到Rust
testFn(ipcBuffer);

Rust端代码

use napi::bindgen_prelude::Buffer;
use arrow::ipc::reader::StreamReader;
use arrow::record_batch::RecordBatch;

#[napi]
pub fn test_fn(buffer: Buffer) {
    // 创建Arrow IPC流读取器
    let mut reader = StreamReader::new(buffer.as_ref());
    
    // 读取并处理RecordBatch
    if let Some(result) = reader.next() {
        match result {
            Ok(batch) => {
                println!("收到RecordBatch,行数:{}", batch.num_rows());
                // 获取浮点列并读取值
                if let Some(float_col) = batch.column(0).as_any().downcast_ref::<arrow::array::Float32Array>() {
                    println!("第一个值:{}", float_col.value(0));
                }
            }
            Err(e) => println!("解析错误:{}", e),
        }
    }
}

需在Cargo.toml中添加依赖:arrow = "51.0"

关键注意事项

  • Arrow Vector的缓冲区包含有效性位图(可选)、值缓冲区等,手动提取时需明确每个缓冲区的用途
  • 使用IPC序列化方案可兼容Arrow的所有高级特性(如嵌套类型、字典编码等),无需关注底层缓冲区细节

内容的提问来源于stack exchange,提问作者lostAstronaut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:50:17