使用 EVCXR 在 REPL 和 Jupyter Notebook 中进行交互式 Rust 编程

更新于 2026-01-15

Richard L. Apodaca. 2020-09-21

交互式编程(interactive programming)是一种强大的工具,允许用户实时输入并求值表达式,从而探索语言特性或解决问题。这种能力通常与 JavaScript 和 Python 等动态解释型语言相关联。像 Java 和 C++ 这样的编译型语言也可以用于交互式编程,但相关工具往往滞后多年。Rust 是一门较新的编译型语言,近年来才开始支持交互式编程。本文将介绍如何借助 EVCXR crate 实现 Rust 的交互式编程。

REPL 与 Notebook

目前,交互式编程主要有两种广泛采用的方式:

  • REPL(“读取-求值-打印循环”,Read-Eval-Print Loop):用户在终端中逐条输入表达式并立即获得求值结果,系统通常提供历史记录以便回溯之前的输入。典型例子包括 Python 解释器和浏览器中的 JavaScript 控制台。
  • Notebook(笔记本):表达式及其输出被组织成若干“单元格”(cells),并在 Web 浏览器中展示。某些表达式可以生成图形化输出,如图像和图表。整个笔记本可保存、发布并重新运行。典型代表包括 Jupyter 和 Pluto。

借助 EVCXR,Rust 现在也能同时支持上述两种交互式编程模式。

REPL

EVCXR 的 REPL 可通过以下命令安装:

$ cargo install evcxr_repl

安装完成后,系统会提供 evcxr 命令。作为演示,下面是一个简单的“Hello, World!”程序,以交互方式编写:

$ evcxr
>> let mut s = String::from("Hello, ");
>> s.push_str("World!");
> s
"Hello, World!"

EVCXR 支持通过 :dep 命令引入私有或公共的 crate。例如,以下代码片段添加了 ChemCore——一个用 Rust 编写的化学信息学工具包。一旦添加依赖,即可像平常一样使用它:

$ evcxr
>> :dep chemcore = "0.2.1"
>> :dep gamma = "0.6.1"
>> extern crate chemcore;
>> use chemcore::molecule::Molecule;
>> use gamma::graph::Graph;
>> let m = chemcore::daylight::read(&"[13c]1ccccc1").unwrap();
>> m.degree(0)
Ok(2)
>> m.neighbors(0)
Ok([5, 1])
>> m.edges()
[(0, 5), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]

额外依赖的 gamma(ChemCore 所基于的图论库)使得 Molecule 类型的图方法进入作用域。

Jupyter 内核

Jupyter 的扩展称为“内核”(kernels)。EVCXR 提供了一个 Jupyter 内核,使 Rust 能在 Notebook 中使用。安装过程是对近期某篇教程中所述步骤的直接改编:

$ conda create --name evcxr
$ conda activate evcxr
$ conda install -y -c conda-forge nb_conda_kernels
$ cargo install evcxr_jupyter
$ evcxr_jupyter --install
$ jupyter notebook

EVCXR 项目维护了一份 Jupyter 使用指南(tour),展示了该内核的各项功能。前一节中的 REPL 示例应可不加修改地在 Jupyter 中运行。

EVCXR Jupyter 内核一个非常实用的功能是支持自定义的基于 HTML 的显示。要启用此功能,只需为感兴趣的类型实现 Debug trait。任何能用 HTML 渲染的内容都可以在单元格的输出区域中显示。这一特性在化学信息学领域尤其吸引人,因为许多数据类型都有其偏好的图形化表示方式。

举个简单例子,一个 Rust 的 Vec(向量)可以用 HTML 有序列表来表示。

EVCXR 内核支持为用户自定义的类型提供专门的显示例程。只需按如下方式实现 Debug trait 即可:

use std::fmt::Debug;

pub struct List<T>(Vec<T>);

impl<T: Debug> List<T> {
    pub fn evcxr_display(&self) {
        let mut html = String::new();
        
        html.push_str("<ol>");
        
        for item in &self.0 {
            html.push_str(&format!("<li>{:?}</li>", item));
        }
        
        html.push_str("</ol>");
        println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT", html);
    }
}

以这种方式实现 Debug 的类型将渲染为 HTML。位图图像(raster images)、SVG 以及 HTML 片段均可内联显示。

例如:

let list = List(vec!["the good", "the bad", "the ugly"]);

list

将显示为:

  • "the good"
  • "the bad"
  • "the ugly"

EVCXR 内核文档列出了几个支持自定义显示的项目。然而据我亲测,其中只有一个按描述正常工作。Petgraph 是一个图数据结构库,我之前曾做过评测。而 petgraph-evcxr 则使得在 Jupyter 单元格中可视化 Petgraph 图成为可能。

:dep petgraph = "*"
:dep petgraph-evcxr = "*"
extern crate petgraph;
use petgraph::graph::Graph;
use petgraph::dot::Dot;
use petgraph_evcxr::{draw_graph, draw_dot};
let mut g : Graph<&str, &str> = Graph::new();
let good = g.add_node("the good");
let bad = g.add_node("the bad");
let ugly = g.add_node("the ugly");
g.add_edge(good, bad, "don't move");
g.add_edge(bad, good, "don't move");
g.add_edge(bad, ugly, "don't move");
g.add_edge(ugly, bad, "don't move");
g.add_edge(ugly, good, "don't move");
g.add_edge(good, ugly, "don't move");
draw_graph(&g);

image

能做 ≠ 应该做

EVCXR 是一款出色的工具,能发挥巨大作用。但和所有工具一样,它也有局限性。最重要的一点是:在 REPL 或 Notebook 中编写大型程序并不是一种理想的体验。对于较长的 Rust 代码,使用专业的开发工具(如 IDE)通常能显著提高生产力。将 REPL 和 Notebook 环境保留用于高层级的操作和探索,才能充分发挥各类工具的优势。

更多资源

尽管 EVCXR 出现时间不长,却已催生了一本图书:《Data Analysis with Rust Notebooks》(《使用 Rust Notebook 进行数据分析》)。我尚未阅读此书,但书中包含多个使用 EVCXR 进行数据处理与可视化的示例,并且据说还附带一些视频。

另一个 Rust REPL 是 Papyrus。目前已有一本关于其安装与使用的书籍可供参考。

除了 REPL 和 Notebook,Rust 社区还涌现出一批脚本语言,不同程度地融合了上述两种交互方式的便利性。例如:

  • Rhai
  • Forge
  • Gluon
  • Dyon

此外,还有一个虽不那么交互但值得一提的替代方案:Rust Playground。这是一个 Web 应用,接受 Rust 代码作为输入并显示生成的输出。它虽然不是真正的 REPL 或 Notebook,但在某些场景下也能起到类似作用。

结语

Rust 正在构建日益丰富的交互式编程生态,包括 EVCXR REPL、Jupyter Rust 内核以及多种脚本语言。在特定约束条件下,这些工具能让 Rust 代码的使用过程更加直观和即时。初学者可以几乎零门槛地直接尝试各种示例;而经验丰富的 Rust 开发者也能通过交互式代码片段,向更广泛的受众展示自己的成果。