宝宝的第一款 Rust+WebAssembly 模块:向 JSConf EU 说 hello!

一个秘密项目一直在为 JSConf EU 酝酿,这个周末就是它的揭晓时刻…

The Arch 是一种超大型体验,它使用 30,000 个彩色 LED 灯来创建一个用于灯光动画的画布。

而且,可以掌控这个空间。使用模块,您可以创建灯光动画。

虽然这是 JSConf,但这些动画不仅仅由 JavaScript 模块驱动。事实上,我们希望您尝试一些新的东西…… Rust + WebAssembly。

Rust logo and JS logo with a heart in between

为什么是这个项目呢?

当您学习一门新的编程语言时,最难的问题之一是找到一个既可以教您基础知识,又能足够有趣让您继续学习更多知识的项目。“Hello World” 只在最初几次有趣……它对现实世界没有真正的影响。

但是,如果您的 Hello World 可以对现实世界产生影响呢?如果它可以控制像这样一个结构呢?

所以让我们开始进行宝宝的第一款 Rust 到 WebAssembly 模块的旅程。

A baby putting together JS and WebAssembly blocks

从某种意义上说,这个项目非常适合您的第一个 WebAssembly 项目……但并非因为这是您使用 WebAssembly 的项目类型。

人们通常使用 WebAssembly 是因为他们想要为自己的应用程序注入活力,让它运行得更快。或者因为他们想要在 Web 和不同的设备(具有不同的操作系统)上使用相同的代码。

这个项目没有做到这两点。

WebAssembly benefits: fast and portable crossed out in red

这个项目之所以适合入门 WebAssembly,并不是因为它是你会使用 WebAssembly 的地方。

相反,它之所以有用是因为它为您提供了关于 JavaScript 和 WebAssembly 如何协同工作的心理模型。所以让我们来看看我们需要做什么来使用 WebAssembly 控制这个空间。然后我会解释为什么这使其成为 WebAssembly 和 JavaScript 如何协同工作的一个很好的心理模型。

时空连续体作为字节

我们这里有一个 3D 空间。或者更确切地说,如果您仔细想想,它更像是一个四维空间,因为我们也在穿越时间。

但是,计算机无法用这四个维度思考。那么,我们如何让这四个维度对计算机有意义呢?让我们从第四维度开始,然后逐渐缩小。

您可能熟悉我们使用称为帧的东西让时间成为第四维并对计算机有意义的方式。

屏幕就像一个翻页书。每个帧就像那本翻页书中的一页。

Picture of a flipbook with a smooth animation next to it

在 Web 上,我们谈论的是每秒 60 帧。这是您在屏幕上实现流畅动画所需的帧数。这实际上意味着您有 60 张屏幕快照……即动画在每秒的 60 个时间点上的外观。

在我们的案例中,快照是空间上灯光外观的快照。

所以,我们现在有了空间的快照序列。空间的 3D 表示序列。

现在我们要从 3D 变为 2D。在本例中,这非常容易。我们只需要将空间压平,基本上变成一张大型的方格纸。

所以现在我们已经降到了 2D。我们只需要再缩小一次。

我们可以通过将所有行并排放置来做到这一点。

A grid being turned into a line of rows

现在我们有了这条像素线。我们可以将其放入内存中。因为内存基本上只是一行盒子。

这意味着我们已经将其缩减为一维结构。我们仍然拥有所有我们在二维、三维或四维表示中拥有的数据。只是以不同的方式表示。它是以行的形式表示的。

为什么这是一种学习 WebAssembly 的好模型?线性内存。

这之所以成为 WebAssembly 和 JavaScript 如何协同工作的一个很好的心理模型,是因为 WebAssembly 和 JavaScript 之间的主要通信方式之一是通过称为线性内存的东西。它基本上是一行内存,您可以使用它来表示事物。

WebAssembly 模块和正在运行它的 JavaScript 都可以访问这个对象。

这是一个名为 ArrayBuffer 的 JavaScript 对象。数组缓冲区只是一个字节数组,字节只是数字。所以要让这个动画发生,JavaScript 会告诉 WebAssembly 模块:“好的,现在填充动画。”

它将通过调用 WebAssembly 模块上的方法来完成此操作。

WebAssembly 会转而去填充线性内存中每个像素的所有颜色。

然后 JavaScript 代码可以提取这些颜色并将它们转换为将被发送到空间的 JSON 数组。

让我们看看如何在 JS 中使用这些数据。

线性内存,困难的方式

如果您自己完成所有操作,而不使用任何库,那么您将直接使用线性内存。

这个线性内存只是一行长长的 1 和 0。当您想要从这些 1 和 0 中创建意义时,您必须弄清楚如何将它们拆分。您所做的是在 ArrayBuffer 上创建一个类型化数组视图。

这实际上只是告诉 JavaScript 如何将这个 ArrayBuffer 中的位拆分。这就像在位周围画框,来说明哪些位属于哪个数字。

例如,如果您使用的是十六进制值,那么您的数字将是 24 位宽。所以您需要一个可以容纳 24 位的框。每个框将包含一个像素。

最小的适合的框是 32 位。所以我们将在缓冲区上创建一个 Int32 视图。这将把位包装成盒子。在这种情况下,我们将不得不添加一些填充来填充它(我没有显示出来,但会有一些额外的零)。

相反,如果我们使用 RGB 值,则框将只有 8 位宽。要获取一个 RGB 值,您将取每三个框,并将它们用作 R-G-和 B 值。这意味着您将迭代这些框并提取数字。

由于我们在这里采取困难的方式,因此您需要编写代码来执行此操作。代码将迭代线性内存并将数据移动到更合理的数据结构中。

对于像这样的项目来说,这并不太糟糕。颜色很好地映射到数字,因此很容易在线性内存中表示。我们使用的数据结构(RGB 值)也不太复杂。但是,当您开始使用更复杂的数据结构时,直接处理内存可能是一件很痛苦的事。

如果您能够将 JS 对象传递给 WebAssembly,并且只让 WebAssembly 操作它,那就容易多了。这在将来是可能的,目前 WebAssembly 社区组正在进行规范工作。

但这并不意味着您必须等到它出现在规范中才能开始使用对象。您今天就可以将对象传递给 WebAssembly 并将其返回到 JS。您只需要添加一个很小的库。

线性内存,简单的方式

这个库被称为 wasm-bindgen。它在 JavaScript 包装器中包装了 WebAssembly 模块。

这个包装器知道如何获取复杂的 JavaScript 对象并将它们写入线性内存。然后,当 WebAssembly 函数返回一个值时,JS 包装器将从线性内存中获取数据并将其转换回 JS 对象。

JS passing the string Hello to <code>wasm-bindgen</code>, which does all of the other work

为此,它会查看 Rust 代码中的函数签名,并准确地找出所需的 JavaScript。这适用于字符串等内置类型。它也适用于您在代码中定义的类型。wasm-bidgen 将获取这些 Rust 结构体 并将其转换为 JavaScript 类。

目前,这个工具专门针对 Rust。但根据它的架构,我们可以为其他语言(如 C/C++)添加对这种高级交互的支持。

总之……

希望您现在已经了解了如何控制这个空间……如何向世界说 Hello World,以及向 WebAssembly 的世界说 hello。

在结束之前,我确实想感谢那些让这个项目成为可能的人。

这个项目的最初想法来自于我在匹兹堡参加的一个类似空间的舞会。但这个项目之所以能够实现,是因为一群出色的人聚集在一起将其变成了现实。

  • Sandra Persing - 我带着一个愿景来到她这里,她让这个愿景变成了现实
  • Dan Brown 和 Maciej Pluta,他们接过了这个愿景,将其变成了比我最初想象的更加令人兴奋和引人入胜的东西
  • Till Schneidereit,他帮助我弄清楚所有部分如何拼凑在一起
  • Josh Marinacci,他创建了网站,并使控制这个空间成为可能
  • Dan Callahan,他以他的开发和调试魔法加入了进来,以确保所有部分都能协同工作
  • Trevor F Smith,他创建了虚拟空间,使每个人都能体验 Arch,即使他们不在活动现场
  • Michael Bebenita 和 Yury Delendik,他们在 WebAssembly Studio 上的工作使与全新的受众共享 WebAssembly 成为可能
  • Rustaceans:Alex Crichton、Ashley Williams、Sarah Meyers、Jan-Erik Rediger、Florian Gilcher、Steve Klabnik、Fabian、Istvan 'Flaki' Szmozsanszky,他们在 WebAssembly Studio 的 Rust 集成方面进行了工作,并帮助新的、有抱负的 Rust 开发人员提升技能
  • JSConf EU 团队为他们所有努力确保我们能够启动项目而付出
  • Ian Brill,这位艺术家,他的作品启发了这个项目,并且他的辛勤工作确保我们可以与您分享它

关于 Lin Clark

Lin 在 Mozilla 的高级开发部门工作,专注于 Rust 和 WebAssembly。

更多 Lin Clark 的文章…