OHOS_NAPI_RS 用户指南

ohos_napi_rs为用户提供了ArkTS和Rust交互的能力。用户通过#[napi]过程宏轻松完成ArkTS和Rust数据类型转换。

命名约定

Rust 和 ArkTS的代码风格有很大区别,Rust 社区喜欢 snake_case 风格,而 ArkTS社区延用JavaScript ,更喜欢 camelCase 风格。ohos_napi_rs 会自动将 Rust 代码的风格转换为 camelCase 风格。

#[napi]
fn rust_add(a: u32, b: u32) -> u32 {
  a + b
}

#[napi(constructor)]
pub struct NapiStruct {
  pub napi_name: String,
  pub napi_kind: u32,
}

index.d.ts

export declare function rustAdd(a: number, b: number): number;
export declare class NapiStruct {
  napiName: string
  napiKind: number
  constructor(napiName: string, napiKind: number)
}

Rust模块显式注册和隐式注册

ArkTS Runtime本身没有提供自动注册机制,需要主动调用napi_module_register进行注册,当前ohos_napi_rs提供显式注册和隐式注册两种方式,默认为隐式注册。

隐式注册

ohos_napi_rs默认机制,在#[napi] 宏展开时默认生成napi_module_register模块注册逻辑,模块名使用CARGO_PKG_NAME环境变量设置的名字。用户什么也不需要做。

显式注册

用户通过feature ohos-explicit-register关闭隐式注册功能,然后自己在代码中调用ohos_napi_rs提供的宏接口ohos_module_register进行显式注册,宏接口参数为模块名,例如。

//Cargo.toml
[dependencies]
napi-derive = { path = "../../crates/macro", features = ["ohos-explicit-register"] }
napi::ohos_module_register!("arkts_napi_sdv");

类型转化

Rust 和 ArkTS类型之间的转换。

ArkTS 类型 Rust类型 备注
undefined Undefined
null Null
boolean bool
number u8、i8、u16、i16、u32、i32、i64、f64
number f32 Rust f32可以转换成ArkTS number,反之不行
bigint BigInt
bigint u64、u128、i128 Rust u64、u128、i128可以转换成ArkTS bigint,反之不行
string String、&'a str Rust &'a str可以转换成ArkTS string,反之不行
string Utf16String
string Latin1String
object Object
Array Vec、Array
ArrayBuffer、ArrayBufferLike Buffer
Uint8Array Uint8Array
Uint8ClampedArray Uint8ClampedArray
Int16Array Int16Array
Uint16Array Uint16Array
Int32Array Int32Array
Uint32Array Uint32Array
Float32Array Float32Array
Float64Array Float64Array
BigInt64Array BigInt64Array
BigUint64Array BigUint64Array
Record HashMap
Promise AsyncTask
Promise async fn
function Function

Undefined

代表 ArkTS中的 undefined

lib.rs

#[napi]
fn return_undefined() -> Undefined {}

index.d.ts

export declare function returnUndefined(): undefined

ets

expect(rustNapi.returnUndefined()).assertEqual(undefined)

Null

代表 ArkTS中的 null

lib.rs

#[napi]
fn return_null() -> Null {
  Null
}

index.d.ts

export declare function returnNull(): null

ets

expect(rustNapi.returnNull()).assertEqual(null)

Numbers

ArkTSNumber 等同于这些 Rust 整数/浮点数 类型: u8、i8、u16、i16、u32、i32、i64、f64。 Rust f32可以转换成ArkTS number,反之不行,会丢失精度。

lib.rs

#[napi]
fn add(a: u32, b: u32) -> u32 {
  a + b
}

index.d.ts

export declare function add(a: number, b: number): number;

ets

let res1 = rustNapi.add(2, 4);
expect(res1).assertEqual(6);

BigInt

Rust 中传递 BigInt 的唯一方法是使用 BigInt 类型。Rust u64、u128、i128可以转换成ArkTS bigint,反之不行,转换时会丢失精度。

lib.rs

#[napi]
fn bigint_add(a: BigInt, b: BigInt) -> u128 {
  a.get_u128().1 + b.get_u128().1
}

#[napi]
pub fn bigint_from_i64() -> BigInt {
  BigInt::from(100i64)
}

index.d.ts

export declare function bigintAdd(a: bigint, b: bigint): bigint
export declare function bigintFromI64(): bigint

ets

expect(rustNapi.bigintAdd(BigInt('1111111111'), BigInt('2222222222'))).assertEqual(BigInt('3333333333'))
expect(rustNapi.bigintFromI64()).assertEqual(BigInt('100'))

String

代表 ArkTS的 String 类型。

lib.rs

#[napi]
fn concat_str(mut s: String) -> String {
  s.push_str(" + Rust 🦀 string!");
  s
}

index.d.ts

export declare function concatStr(s: string): string

ets

let res = rustNapi.concatStr('æ¶½¾DEL');
expect(res).assertEqual('æ¶½¾DEL + Rust 🦀 string!');

Boolean

代表 ArkTSBoolean 类型。

lib.rs

#[napi]
fn contains(source: String, target: String) -> bool {
  source.contains(&target)
}

index.d.ts

export declare function contains(source: string, target: string): boolean

ets

let res = rustNapi.contains('hello', 'ell');
expect(res).assertTrue();

Buffer

lib.rs

#[napi]
fn get_buffer() -> Buffer {
  String::from("Hello world").as_bytes().into()
}

#[napi]
fn append_buffer(buf: Buffer) -> Buffer {
  let mut buf = Vec::<u8>::from(buf);
  buf.push(b'!');
  buf.into()
}

index.d.ts

export declare function getBuffer(): ArrayBuffer
export declare function appendBuffer(buf: ArrayBuffer): ArrayBuffer

ets

let buf = rustNapi.getBuffer()
expect(buf.toString()).assertEqual('Hello world')

let temp = buffer.from('Hello world').buffer
buf = rustNapi.appendBuffer(temp)
expect(buf.toString()).assertEqual('Hello world!')

Object

代表 Arkts匿名对象值。

lib.rs

#[napi]
fn list_obj_keys(obj: Object) -> Vec<String> {
  Object::keys(&obj).unwrap()
}

#[napi]
fn create_obj(env: Env) -> Object {
  let mut obj = env.create_object().unwrap();
  obj.set("test", 1).unwrap();

  obj
}

index.d.ts

export declare function listObjKeys(obj: object): Array<string>
export declare function createObj(): object

ets

let res1 = rustNapi.listObjKeys({ name: 'John Doe', age: 20 });
expect(res1).assertDeepEquals(['name', 'age']);

let res2 = rustNapi.createObj()
expect(res2).assertDeepEquals({ test: 1 });

如果您想要用 Rust 中定义的结构来转换 Arkts中的对象,您可以使用 #[napi] 宏里面的 object 属性。

lib.rs

/// #[napi(object)] 需要所有的结构体字段都是对外可见的
#[napi(object)]
pub struct StrictObject {
  pub name: String,
}

#[napi]
pub fn receive_strict_object(strict_object: StrictObject) {
  assert_eq!(strict_object.name, "strict");
}

index.d.ts

export interface StrictObject {
  name: string
}
export declare function receiveStrictObject(strictObject: StrictObject): void

ets

rustNapi.receiveStrictObject({ name: 'strict' })

注意:Rust fn 中传入的 #[napi(object)] 结构体是从ArkTS克隆的, 对其的任何更改都不会影响到原始的ArkTS对象。

Array

lib.rs

#[napi]
fn sum_nums(nums: Vec<u32>) -> u32 {
  nums.iter().sum()
}

#[napi]
pub fn get_words() -> Vec<&'static str> {
  vec!["foo", "bar"]
}

index.d.ts

export declare function sumNums(nums: Array<number>): number
export declare function getWords(): Array<string>

ets

let res1 = rustNapi.sumNums([1, 2, 3, 4, 5])
expect(res1).assertEqual(15)

let res2 = rustNapi.getWords()
expect(res2).assertDeepEquals(['foo', 'bar'])

TypedArray

lib.rs

#[napi]
fn convert_u32_array(input: Uint32Array) -> Vec<u32> {
  input.to_vec()
}

#[napi]
fn mutate_typed_array(mut input: Float32Array) {
  for item in input.as_mut() {
    *item *= 2.0;
  }
}

#[napi]
fn create_external_typed_array() -> Uint32Array {
  Uint32Array::new(vec![1, 2, 3, 4, 5])
}

#[napi]
fn u32_array_to_array(input: &[u32]) -> Vec<u32> {
  input.to_vec()
}

index.d.ts

export declare function convertU32Array(input: Uint32Array): Array<number>
export declare function mutateTypedArray(input: Float32Array): void
export declare function createExternalTypedArray(): Uint32Array
export declare function u32ArrayToArray(input: Uint32Array): Array<number>

ets

const input = new Uint32Array([1, 2, 3, 4, 5])
expect(rustNapi.convertU32Array(input)).assertDeepEquals(Array.from(input))

let input: Float32Array = new Float32Array([1, 2, 3, 4, 5])
rustNapi.mutateTypedArray(input)
expect(input).assertDeepEquals(new Float32Array([2.0, 4.0, 6.0, 8.0, 10.0]))

expect(rustNapi.createExternalTypedArray()).assertDeepEquals(new Uint32Array([1, 2, 3, 4, 5]))

expect(rustNapi.u32ArrayToArray(new Uint32Array([1, 2, 3]))).assertDeepEquals([1, 2, 3])

与 object不同,传递给 Rust 的 TypedArray 是一个 引用, 不会执行任何数据 CopyClone,对 TypedArray 的每次更改都会反映到原始的 JavaScript TypedArray

HashMap

lib.rs

#[napi]
fn get_mapping() -> HashMap<String, u32> {
  let mut map = HashMap::new();
  map.insert("a".to_string(), 101);
  map.insert("b".to_string(), 102);
  map
}

#[napi]
fn sum_mapping(nums: HashMap<String, u32>) -> u32 {
  nums.into_values().sum()
}

index.d.ts

export declare function getMapping(): Record<string, number>
export declare function sumMapping(nums: Record<string, number>): number

ets

let res1 = rustNapi.getMapping()
expect(res1).assertDeepEquals({ 'a': 101, 'b': 102 })

let res2 = rustNapi.sumMapping({ 'a': 101, 'b': 102 })
expect(res2).assertEqual(203)

默认 constructor

如果一个 Rust 结构体中的所有字段都是 pub,那么您可以使用 #[napi(constructor)] 来使 struct 有一个默认的 constructor

lib.rs

#[napi(constructor)]
pub struct AnimalWithDefaultConstructor {
  pub name: String,
  pub kind: u32,
}

index.d.ts

export declare class AnimalWithDefaultConstructor {
  name: string
  kind: number
  constructor(name: string, kind: number)
}

ets

let n = new rustNapi.AnimalWithDefaultConstructor('jony', rustNapi.Kind.Duck)
expect(n.name).assertEqual('jony')
expect(n.kind).assertEqual(rustNapi.Kind.Duck)

自定义 constructor

如果您想定义一个自定义的 constructor,您可以在结构体的 impl 块中的构造函数 fn 上面使用 #[napi(constructor)]

lib.rs

#[napi]
pub struct Bird {
  pub name: String,
}

#[napi]
impl Bird {
  #[napi(constructor)]
  pub fn new(name: String) -> Self {
    Bird { name }
  }
}

index.d.ts

export declare class Bird {
  name: string
  constructor(name: string)
}

ets

let bird = new rustNapi.Bird('Carolyn')

工厂

除了 constructor 之外,您还可以使用 #[napi(factory)]Class 上定义工厂方法。

lib.rs

#[napi]
pub struct ClassWithFactory {
  pub name: String,
}

#[napi]
impl ClassWithFactory {
  #[napi(factory)]
  pub fn with_name(name: String) -> Self {
    Self { name }
  }

  #[napi]
  pub fn set_name(&mut self, name: String) -> &Self {
    self.name = name;
    self
  }
}

index.d.ts

export declare class ClassWithFactory {
  name: string
  static withName(name: string): ClassWithFactory
  setName(name: string): this
}

ets

let duck = rustNapi.ClassWithFactory.withName('Default')
expect(duck.name).assertEqual('Default')

let ret = duck.setName('D')
expect(duck.name).assertEqual('D')
expect(ret).assertEqual(duck)

class Method、Getter和Setter

您可以在 Rust 的结构体方法上使用 #[napi] 定义一个 ArkTS 类方法。使用 #[napi(getter)] 定义ArkTS类的getter,使用 #[napi(setter)] 定义ArkTS类的setter。

lib.rs

#[napi]
pub struct Animal {
  name: String,
}

#[napi]
impl Animal {
  #[napi(constructor)]
  pub fn new(name: String) -> Self {
    Animal { name }
  }

  #[napi(getter)]
  pub fn get_name(&self) -> &str {
    self.name.as_str()
  }

  #[napi(setter)]
  pub fn set_name(&mut self, name: String) {
    self.name = name;
  }
    
  #[napi]
  pub fn whoami(&self) -> String {
    self.name
  }
}

index.d.ts

export declare class Animal {
  constructor(name: string)
  get name(): string
  set name(name: string)
  whoami(): string
}

ets

let dog = new rustNapi.Animal('a')
expect(dog.name).assertEqual('a')
expect(dog.whoami()).assertEqual('a')
dog.name = 'b'
expect(dog.name).assertEqual('b')

类作为参数

ClassObject不同, Class 可以有关联Rust 方法。Class 中的每个字段都可以在 ArkTS中被修改。

因此,当您创建类时,该类的所有权实际上已转移到 ArkTS端,它由 ArkTS GC 管理,您只能通过传递其引用将其传回。

lib.rs

#[napi]
impl RefBird {
  #[napi(constructor)]
  pub fn new(age: u32) -> Self {
    RefBird { age }
  }

  #[napi]
  pub fn get_age(&self) -> u32 {
    self.age
  }

  #[napi]
  pub fn set_age(&mut self, age: u32) {
    self.age = age;
  }
}

#[napi]
pub fn class_ref1(bird: &mut RefBird) {
  bird.set_age(200)
}

#[napi]
pub fn class_ref2(bird: &RefBird) -> u32 {
  bird.get_age()
}

index.d.ts

export declare class RefBird {
  constructor(age: number)
  getAge(): number
  setAge(age: number): void
}

export declare function classRef1(bird: RefBird): void

export declare function classRef2(bird: RefBird): number

ets

let bird = new rustNapi.RefBird(10)
expect(bird.getAge()).assertEqual(10)

rustNapi.classRef1(bird)
expect(bird.getAge()).assertEqual(200)

let res = rustNapi.classRef2(bird)
expect(res).assertEqual(200)

自定义终结逻辑

当 ArkTS对象被垃圾回收时,会释放 ArkTS 对象中封装的 Rust 结构体,您还可以为 Rust 结构体指定自定义终结逻辑。

lib.rs

#[napi(custom_finalize)]
pub struct CustomFinalize {
  width: u32,
  height: u32,
  inner: Vec<u8>,
}

#[napi]
impl CustomFinalize {
  #[napi(constructor)]
  pub fn new(width: u32, height: u32) -> Result<Self> {
    let inner = vec![0; (width * height * 4) as usize];
    Ok(Self {
      width,
      height,
      inner,
    })
  }
}

impl ObjectFinalize for CustomFinalize {
  fn finalize(self, _env: Env) -> Result<()> {
    oh_log_info!("{:?}", self.inner);
    Ok(())
  }
}

index.d.ts

export declare class CustomFinalize {
  constructor(width: number, height: number)
  getSize(): number
}

ets

let c = new rustNapi.CustomFinalize(200, 200);

枚举

不支持将 Rust enumimpl 生成到 ArkTS中。

普通枚举

Rust enum 被转换为普通的 ArkTS对象。

lib.rs

#[napi]
pub enum Kind {
  Dog,
  Cat,
  Duck,
}

index.d.ts

export declare const enum Kind {
  Dog = 0,
  Cat = 1,
  Duck = 2
}

ets

let res = [rustNapi.Kind.Dog, rustNapi.Kind.Cat, rustNapi.Kind.Duck]
expect(res).assertDeepEquals([0, 1, 2])

字符串枚举

lib.rs

#[napi(string_enum)]
pub enum Status {
  Pristine,
  Loading,
  Ready,
}

#[napi]
fn enum_to_status() -> Status {
  Status::Loading
}

index.d.ts

export declare const enum Status {
  Pristine = 'Pristine',
  Loading = 'Loading',
  Ready = 'Ready'
}
export declare function enumToStatus(): Status

ets

let res = rustNapi.enumToStatus();
expect(res).assertEqual(rustNapi.Status.Loading);

函数

ArkTS可以传递一个函数到Rust侧执行。

lib.rs

#[napi]
pub fn call_function(cb: Function<(), u32>) -> Result<u32> {
  cb.call(())
}

#[napi]
pub fn call_function_with_arg(cb: Function<(u32, u32), u32>, arg0: u32, arg1: u32) -> Result<u32> {
  cb.call((arg0, arg1))
}

index.d.ts

export declare function callFunction(cb: () => number): number
export declare function callFunctionWithArg(cb: (arg0: number, arg1: number) => number, arg0: number, arg1: number): number

ets

let res1 = rustNapi.callFunction(() => 42);
expect(res1).assertEqual(42);

let res2 = rustNapi.callFunctionWithArg((a, b) => a + b, 42, 10);
expect(res2).assertEqual(52);

线程安全函数

线程安全函数机制,允许用户将ArkTS函数传递给Rust侧,并且在Rust的任意线程中执行该ArkTS函数。

lib.rs

#[napi]
pub fn call_threadsafe_function(callback: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> =
    callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value + 1]))?;
  for n in 0..100 {
    let tsfn = tsfn.clone();
    thread::spawn(move || {
      tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking);
    });
  }
  Ok(())
}

index.d.ts

export declare function callThreadsafeFunction(callback: (err: Error|null, arg: number) => void): void

ets

let i: number = 0
let value: number = 0
let p: Promise<number> = new Promise((resolve: Function) => {
    rustNapi.callThreadsafeFunction((_, v) => {
        i++
        value += v
        if (i === 100) {
            resolve(value)
        }
    })
})
let res = await p;
expect(res).assertEqual(5050)

ErrorStrategy

Threadsafe Function 有两种不同的错误处理策略,您可以在 ThreadsafeFunction 的第二个泛型参数中定义策略:

lib.rs

let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> = ...

ErrorStrategy::CalleeHandled

Rust 代码中的 Err 将被传递到 ArkTS回调的参数中。使用 ErrorStrategy::CalleeHandled,您必须使用 Result 类型调用 ThreadsafeFunction, 这样 Error 才会被处理并传递回 ArkTS回调:

lib.rs

#[napi]
pub fn call_threadsafe_function(callback: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> =
    callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value + 1]))?;
  for n in 0..100 {
    let tsfn = tsfn.clone();
    thread::spawn(move || {
      tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking);
    });
  }
  Ok(())
}

index.d.ts

export declare function callThreadsafeFunction(callback: (err: Error|null, arg: number) => void): void

ErrorStrategy::Fatal

不传递 Error 给 ArkTS端,如果您的代码不会返回 Err ,您可以使用这种策略来避免在 Rust 代码中使用 Ok 封装。

通过这种策略,ThreadsafeFunction 不需要使用 Result<T> 调用,并且ArkTS回调的参数是 Rust 中的值,而不是 Error | null

lib.rs

#[napi]
pub fn threadsafe_function_fatal_mode(cb: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
    cb.create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?;
  thread::spawn(move || {
    tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
  });
  Ok(())
}

index.d.ts

export declare function threadsafeFunctionFatalMode(cb: (arg: boolean) => void): void

ets

let tsfnFatalMode = new Promise<boolean>((resolve) => {
    rustNapi.threadsafeFunctionFatalMode((arg: boolean) => {
        resolve(arg)
    })
})

let res = await tsfnFatalMode;
expect(res).assertEqual(true)

异步任务

异步任务机制,允许用户创建一个异步工作对象,可以在需要执行耗时操作的场景中使用,以避免阻塞主线程。Task 特征提供了一种定义这样的异步任务的方法,该任务需要在 libuv 线程中运行,您可以实现 compute 方法,该方法将在 libuv 线程中调用。

fn compute 方法在 libuv 线程中运行,您可以在这里运行一些繁重的计算,这不会阻塞 ArkTS主线程。 Task 特征上有两个关联类型,type Outputtype JsValueOutputcompute 方法的返回类型,Output会传递给resolve方法,JsValueresolve 方法的返回类型。fn compute 函数中无法回调 ArkTS函数,因为它不在主线程上执行。

lib.rs

struct AsyncSum {
  input: u32,
}

impl Task for AsyncSum {
  type Output = u32;
  type JsValue = JsNumber;

  fn compute(&mut self) -> Result<Self::Output> {
    Ok(task_sum(self.input))
  }

  fn resolve(&mut self, env: Env, output: u32) -> Result<Self::JsValue> {
    env.create_uint32(output)
  }
}

#[napi]
fn async_sum(input: u32) -> AsyncTask<AsyncSum> {
  AsyncTask::new(AsyncSum { input })
}

index.d.ts

export declare function asyncSum(input: number): Promise<number>

ets

let res = await rustNapi.asyncSum(10);
expect(res).assertEqual(45)

除了 computeresolve,您还可以提供 reject 方法,当 Taskcompute遇到错误时,可以执行一些清理工作。您还可以提供一个 finally 方法,在 Taskresolvedrejected 后执行一些操作。

struct AsyncFailed;

impl Task for AsyncFailed {
  type Output = u32;
  type JsValue = JsNumber;

  fn compute(&mut self) -> Result<Self::Output> {
    let err = Error::new(Status::Cancelled, "failed to call compute");
    Err(err)
  }

  fn resolve(&mut self, env: Env, output: u32) -> Result<Self::JsValue> {
    env.create_uint32(output)
  }

  fn reject(&mut self, _env: Env, err: Error) -> Result<Self::JsValue> {
    oh_log_info!("in AsyncSum reject");
    Err(err)
  }

  fn finally(&mut self, _env: Env) -> Result<()> {
    oh_log_info!("in AsyncSum finally");
    Ok(())
  }
}

#[napi]
fn async_failed() -> AsyncTask<AsyncFailed> {
  AsyncTask::new(AsyncFailed)
}

index.d.ts

export declare function asyncFailed(): Promise<number>

ets

try {
    await rustNapi.asyncFailed();
    expect(false).assertTrue();
} catch (err) {
    expect(err.message).assertEqual('failed to call compute');
    expect(err.code).assertEqual('Cancelled')
}

异步函数

为了使用 async fn ,您必须开启 napiasynctokio_rt 特性:

Cargo.toml

[dependencies]
napi = { features = ["async"] }

ohos_napi_rs 默认支持 tokio 运行时,如果您在 async fnawait 一个 tokio futureohos_napi_rs 将在 tokio 运行时中执行它,并将其转换为 ArkPromise

lib.rs

use futures::prelude::*;
use tokio::fs;

#[napi]
async fn read_file_async(path: String) -> Result<Buffer> {
  fs::read(path)
    .map(|r| match r {
      Ok(content) => Ok(content.into()),
      Err(e) => Err(Error::new(
        Status::GenericFailure,
        format!("failed to read file, {}", e),
      )),
    })
    .await
}

index.d.ts

export declare function readFileAsync(path: string): Promise<ArrayBuffer>

ets

import fs from '@ohos.file.fs';

let context = getContext()
let tempDir = context.tempDir;
// 新建并打开文件
let file = fs.openSync(tempDir + '/rust_test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 写入一段内容至文件
fs.writeSync(file.fd, 'Try to write str.');
// 关闭文件
fs.closeSync(file.fd)
let data = await rustNapi.readFileAsync(tempDir + '/rust_test.txt')
expect(data).assertDeepEquals(buffer.from('Try to write str.').buffer);

Await Promise

支持在 Rust 中 await 一个 ArkTS Promise

lib.rs

#[napi]
pub async fn async_plus_100(p: Promise<u32>) -> Result<u32> {
  let v = p.await?;
  Ok(v + 100)
}

index.d.ts

export declare function asyncPlus100(p: Promise<number>): Promise<number>

ets

let fx = 20
let p = rustNapi.asyncPlus100(
    new Promise((resolve) => {
        setTimeout(() => resolve(fx), 50)
    }),
)

let res = await p;
expect(res).assertEqual(fx + 100)

注入 Env

#[napi] 宏是对 Node-API 的一个非常高级的抽象,大多数情况下,您使用 Rust 的原生 API 和包。但是有时候您仍然需要访问底层的 Node-API

对于这种情况,ohos_napi_rs 允许您通过 #[napi] 装饰,将 Env 注入到您的 fn 中。

lib.rs

#[napi]
pub fn env_create_string(env: Env) -> Result<JsString> {
  env.create_string("env crete string")
}

Env 将会被 ohos_napi_rs 自动注入,这不会影响 ArkTS端的参数类型:

index.d.ts

export declare function envCreateString(): string

ets

expect(rustNapi.envCreateString()).assertEqual('env crete string')

ohos_napi_rs当前不支持特性

ohos_napi_rs还有部分特性没有从napi_rs仓库继承过来,包括:

1、不支持Symbol。

2、不支持Generator。

3、AsyncTask不支持AbortSignal 。

4、compat-mode模式当前未支持。