Classe
Não há o conceito de classe em Rust. Utilizamos struct
para representar uma
Class
JavaScript.
Constructor
constructor
padrão
Se todos os campos em uma struct Rust
forem pub
, então você pode usar #[napi(constructor)]
para fazer com que a struct
tenha um constructor
padrão.
#[napi(constructor)]
pub struct AnimalWithDefaultConstructor {
pub name: String,
pub kind: u32,
}
export class AnimalWithDefaultConstructor {
name: string
kind: number
constructor(name: string, kind: number)
}
constructor
personalizado
Se você quiser definir um constructor
personalizado, pode usar #[napi(constructor)]
na sua constructor fn
no bloco impl
da struct.
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
pub struct QueryEngine {}
#[napi(js_name = "QueryEngine")]
pub struct JsQueryEngine {
engine: QueryEngine,
}
#[napi]
impl JsQueryEngine {
#[napi(constructor)]
pub fn new() -> Self {
JsQueryEngine { engine: QueryEngine::new() }
}
}
export class QueryEngine {
constructor()
}
NAPI-RS atualmente não suporta private constructor
. Seu construtor
personalizado deve ser pub
em Rust.
Factory
Além do constructor
, você também pode definir métodos de fábrica na Class
usando #[napi(factory)]
.
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
pub struct QueryEngine {}
#[napi(js_name = "QueryEngine")]
pub struct JsQueryEngine {
engine: QueryEngine,
}
#[napi]
impl JsQueryEngine {
#[napi(factory)]
pub fn with_initial_count(count: u32) -> Self {
JsQueryEngine { engine: QueryEngine::with_initial_count(count) }
}
}
export class QueryEngine {
static withInitialCount(count: number): QueryEngine
constructor()
}
Se nenhum #[napi(constructor)]
for definido na struct
, e você tentar criar
uma instância (new
) da Class
em JavaScript, um erro será lançado.
import { QueryEngine } from './index.js'
new QueryEngine() // Error: Class contains no `constructor`, cannot create it!
class method
Você pode definir um método de classe JavaScript com #[napi]
em um método de struct em Rust.
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
pub struct QueryEngine {}
#[napi(js_name = "QueryEngine")]
pub struct JsQueryEngine {
engine: QueryEngine,
}
#[napi]
impl JsQueryEngine {
#[napi(factory)]
pub fn with_initial_count(count: u32) -> Self {
JsQueryEngine { engine: QueryEngine::with_initial_count(count) }
}
/// Class method (Método de classe)
#[napi]
pub async fn query(&self, query: String) -> napi::Result<String> {
self.engine.query(query).await
}
#[napi]
pub fn status(&self) -> napi::Result<u32> {
self.engine.status()
}
}
export class QueryEngine {
static withInitialCount(count: number): QueryEngine
constructor()
query(query: string) => Promise<string>
status() => number
}
async fn
precisa dos recursos napi4
e tokio_rt
habilitados.
Qualquer fn
em Rust
que retorne Result<T>
será tratado como T
em JavaScript/TypeScript. Se o Result<T>
for Err
, um erro JavaScript será lançado.
Getter
Defina um getter
de classe JavaScript (opens in a new tab) usando #[napi(getter)]
. A fn
Rust deve ser um método de struct, não uma função associada.
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
pub struct QueryEngine {}
#[napi(js_name = "QueryEngine")]
pub struct JsQueryEngine {
engine: QueryEngine,
}
#[napi]
impl JsQueryEngine {
#[napi(factory)]
pub fn with_initial_count(count: u32) -> Self {
JsQueryEngine { engine: QueryEngine::with_initial_count(count) }
}
/// Método de classe
#[napi]
pub async fn query(&self, query: String) -> napi::Result<String> {
self.engine.query(query).await
}
#[napi(getter)]
pub fn status(&self) -> napi::Result<u32> {
self.engine.status()
}
}
export class QueryEngine {
static withInitialCount(count: number): QueryEngine
constructor()
get status(): number
}
Setter
Defina setter
de classe JavaScript (opens in a new tab) usando #[napi(setter)]
. A fn
Rust deve ser um método de struct, não uma função associada.
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
pub struct QueryEngine {}
#[napi(js_name = "QueryEngine")]
pub struct JsQueryEngine {
engine: QueryEngine,
}
#[napi]
impl JsQueryEngine {
#[napi(factory)]
pub fn with_initial_count(count: u32) -> Self {
JsQueryEngine { engine: QueryEngine::with_initial_count(count) }
}
/// Método de classe
#[napi]
pub async fn query(&self, query: String) -> napi::Result<String> {
self.engine.query(query).await
}
#[napi(getter)]
pub fn status(&self) -> napi::Result<u32> {
self.engine.status()
}
#[napi(setter)]
pub fn count(&mut self, count: u32) {
self.engine.count = count;
}
}
export class QueryEngine {
static withInitialCount(count: number): QueryEngine
constructor()
get status(): number
set count(count: number)
}
Class como argumento
Class
é diferente de Object
. Class
pode ter métodos Rust e funções associadas nele. Cada campo em Class
pode ser mutado em JavaScript.
Portanto a ownership(propriedade) da Class
é realmente transferida para o lado do JavaScript enquanto você a está criando. Ela é gerenciada pelo GC do JavaScript e você só pode passá-la de volta passando sua reference
.
pub fn accept_class(engine: &QueryEngine) {
// ...
}
pub fn accept_class_mut(engine: &mut QueryEngine) {
// ...
}
export function acceptClass(engine: QueryEngine): void
export function acceptClassMut(engine: QueryEngine): void
Atributos de propriedade
Os atributos padrão da propriedade são writable = true
, enumerable = true
e configurable = true
. Você pode controlar os atributos da propriedade através do macro #[napi]
:
use napi::bindgen_prelude::*;
use napi_derive::napi;
// Uma estrutura complexa que não pode ser exposta diretamente ao JavaScript.
#[napi]
pub struct QueryEngine {
num: i32,
}
#[napi]
impl QueryEngine {
#[napi(constructor)]
pub fn new() -> Result<Self> {
Ok(Self {
num: 42,
})
}
// writable / enumerable / configurable
#[napi(writable = false)]
pub fn get_num(&self) -> i32 {
self.num
}
}
Neste caso, o método getNum
de QueryEngine
não é gravável:
import { QueryEngine } from './index.js'
const qe = new QueryEngine()
qe.getNum = function () {} // TypeError: Cannot assign to read only property 'getNum' of object '#<QueryEngine>'
Lógica de Finalização personalizada
O NAPI-RS descartará a struct Rust envolvida no objeto JavaScript quando o objeto JavaScript for "garbage collected". Você também pode especificar uma lógica de finalização personalizada para a struct Rust.
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi(custom_finalize)]
pub struct CustomFinalize {
width: u32,
height: u32,
inner: Vec<u8>,
}
#[napi]
impl CustomFinalize {
#[napi(constructor)]
pub fn new(mut env: Env, width: u32, height: u32) -> Result<Self> {
let inner = vec![0; (width * height * 4) as usize];
let inner_size = inner.len();
env.adjust_external_memory(inner_size as i64)?;
Ok(Self {
width,
height,
inner,
})
}
}
impl ObjectFinalize for CustomFinalize {
fn finalize(self, mut env: Env) -> Result<()> {
env.adjust_external_memory(-(self.inner.len() as i64))?;
Ok(())
}
}
Primeiro, você pode definir o atributo custom_finalize
no macro #[napi]
, e o NAPI-RS não gerará o ObjectFinalize
padrão para a estrutura Rust.
Então, você pode implementar o ObjectFinalize
você mesmo para a Rust struct.
Neste caso, a estrutura CustomFinalize
aumenta a memória externa no constructor e a diminui em fn finalize
.
instance of
Há um fn instance_of
em todas as classes #[napi]
:
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi]
pub struct NativeClass {}
#[napi]
pub fn is_native_class_instance(env: Env, value: Unknown) -> Result<bool> {
NativeClass::instance_of(env, value)
}
import { NativeClass, isNativeClassInstance } from './index.js'
const nc = new NativeClass()
console.log(isNativeClassInstance(nc)) // true
console.log(isNativeClassInstance(1)) // false