【问题标题】:Is it possible to change generic lifetime of a struct?是否可以更改结构的通用生命周期?
【发布时间】:2022-01-01 15:43:54
【问题描述】:

我有一个现有的库,它通过引用具有生命周期参数的结构来公开必要的数据。问题是我需要使用wasm_bindgen 公开它。为此,我在我的 wasm 模块中创建了一个单独的结构:

#[wasm_bindgen]
struct Event {
  // doesn't compile - passed from another lib
  inner: &'a InnerEvent<'b>
}

impl<'a,'b> From<&'a InnerEvent<'b>> for Event {
  fn from(e: &'a InnerEvent<'b>) -> Self {
    Event { inner: e }
  }
}

现在,问题在于 'a'b 是从外部库传递的生命周期,但是 wasm-bindgen 的限制不允许即将通过 WebAssembly 访问的 Rust 结构包含任何泛型参数本身 - 因此Event 不能定义对'a'b 的任何引用。

虽然我可以通过使用原始指针来摆脱'a,但我不能对'b 做同样的事情。从技术上讲,我可以用'static 模拟它,但是我无法将结构的通用生命周期参数从'a 更改为'static

如何以不与 wasm-bindgen 限制冲突的方式保持对 &amp;'a InnerEvent&lt;'b&gt; 的引用?

编辑

我的特定用例如下所示 - 我正在为库编写一个包装器,它为自定义用户数据启用 pub/sub:

pub struct Transaction<'txn> {
  // .. omitted fields
}

pub struct SomeStruct {
  pub fn subscribe<F>(&mut self, f: F) -> Subscription
    where F: Fn(&Transaction, &Event) -> () + 'static {
    // .. method body
  }
}

现在我需要将 SomeStruct 暴露给 WebAssembly。为此,我正在创建一个 wasm-bingen 包装器,并且我需要它能够将其订阅功能暴露给 JavaScript 端:

#[wasm_bindgen]
pub struct SomeStructWrapper(SomeStruct);

#[wasm_bindgen]
impl SomeStructWrapper {
  #[wasm_bindgen]
  pub fn observe(&mut self, f: js_sys::Function) -> SubscriptionWrapper {
    let sub = self.0.observe(move |transaction, event| {
      // transaction is: &'a Transaction<'txn> and event: &'a Event
      let e = EventWrapper::new(e, txn);
      let arg: JsValue = e.into();
      f.call1(&JsValue::UNDEFINED, &arg);
    });
    SubscriptionWrapper(sub)
  }
}

#[wasm_bindgen]
pub struct SubscriptionWrapper(Subscription);

现在的问题是我需要在 JavaScript 回调中引用两个 rust 回调参数(事务和事件)。这意味着EventWrapper 需要将它们存储为字段:

// code below won't compile because
// wasm_bindgen doesn't allow its structs to declare lifecycle parameters
#[wasm_bindgen]
pub struct EventWrapper {
  transaction: &'a Transaction<'txn>,
  inner_event: &'a Event,
}

// we can get rid of 'a by casting references to raw pointers
// but this won't fix issue with 'txn lifecycle
#[wasm_bindgen]
pub struct EventWrapper {
  transaction: *const Transaction<'txn>,
  inner_event: *const Event,
}

【问题讨论】:

  • 我建议在接触 wasm rust 之前学习 Rust,特别是因为 wasm rust 已经冻结了 > 1 年,并且最近才再次移动,你几乎找不到好的资源。
  • 也许是一些非标准的用例,不确定是否忽略了一些东西,但 #[wasm_bindgen] 用于创建与 JavaScript 的粘合。如果现有的库是 Rust,它只是普通的接口还是?也许是一个最小的 repo,它说明了这个问题可以解释。
  • 也许考虑包装例如与 Rc 的事务,可能与 Cell 或 RefCell 的额外事务,然后在构建 JavaScript 闭包时克隆,例如像这里github.com/rustwasm/wasm-bindgen/blob/main/examples/paint/src/… 这是在发送到 JavaScript 的闭包中处理静态生命周期要求的常见模式。

标签: rust wasm-bindgen


【解决方案1】:

在下面的代码中,它得到错误`error: structs with #[wasm_bindgen]目前不能有生命周期或类型参数?

use wasm_bindgen::prelude::*;

struct  InnerEvent<'a> {
    _a: &'a str
}

#[wasm_bindgen]
struct Event<'a,'b> where 'a: 'b {
  inner: &'a InnerEvent<'b>
}

impl<'a,'b> From<&'a InnerEvent<'b>> for Event<'a,'b> {
  fn from(e: &'a InnerEvent<'b>) -> Self {
    Event { inner: e }
  }
}

输出

error: structs with #[wasm_bindgen] cannot have lifetime or type parameters currently
 --> src\lib.rs:8:13
  |
8 | struct Event<'a,'b> where 'a: 'b {
  |             ^^^^^^^

你可以像这样到处走走

use wasm_bindgen::prelude::*;

struct  InnerEvent<'a> {
    _a: &'a str
}

struct Event<'a,'b> where 'a: 'b {
  inner: &'a InnerEvent<'b>
}

impl<'a,'b> From<&'a InnerEvent<'b>> for Event<'a,'b> {
  fn from(e: &'a InnerEvent<'b>) -> Self {
    Event { inner: e }
  }
}

#[wasm_bindgen]
struct WrapEvent
{
    i: Event<'static, 'static>
}

但这符合您的要求吗?

这里也讨论了这个问题How to get rid of lifetime in wrapper struct for wasm_bindgen

【讨论】:

  • 虽然这是一些想法,但它不能完全解决问题。我已经编辑了原始问题以提供更多详细信息。
猜你喜欢
  • 2023-03-03
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-18
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
相关资源
最近更新 更多