GPUI:用 Rust 构建高性能跨平台桌面应用

---
title: GPUI:用 Rust 构建高性能跨平台桌面应用
published_at: 2026-08-14
language: zh
---

# GPUI:用 Rust 构建高性能跨平台桌面应用

做跨平台桌面应用,最常见的选择是把一个 Web 应用放进桌面窗口里。Electron 直接带上 Chromium,Tauri 使用系统 WebView。这样做很实用:前端生态成熟,开发速度快,组件和调试工具都现成。

但这条路也有天然代价。应用界面本质上还是网页,运行时要经过浏览器或 WebView;当产品变成编辑器、IDE、AI 工具、设计工具这类高频交互应用时,启动速度、内存占用、绘制性能、复杂状态管理和原生体验都会变成更核心的问题。

GPUI 选择了另一条路线:不用浏览器作为 UI 层,而是用 Rust 直接描述应用状态、界面结构和交互,再交给 GPU 渲染。

它来自 Zed 编辑器项目。这个背景很重要,因为代码编辑器不是简单表单应用,而是由文本渲染、复杂布局、多窗口、快捷键、命令面板、文件树、异步任务和高频刷新组成的生产力工具。GPUI 的设计目标,正是支撑这类复杂桌面软件。

## GPUI 的基本定位

GPUI 是一个 Rust 原生、GPU 加速的桌面 UI 框架。

它不是 Electron 这样的浏览器壳:

```text
Electron
  = Chromium + Node.js + Web 页面
```

也不是 Tauri 这样的 WebView 架构:

```text
Tauri
  = Rust 后端 + 系统 WebView 前端
```

GPUI 的路径更直接:

```text
GPUI
  = Rust 应用状态 + Rust UI 描述 + 平台窗口 + GPU 渲染
```

这意味着应用的主要逻辑、状态和界面可以放在同一种语言、同一个类型系统里组织。你不需要在 Rust 后端和 JavaScript 前端之间来回切换,也不需要把桌面界面先翻译成网页模型。

## 一个 GPUI 应用如何启动

GPUI 应用从 `Application` 开始。它负责启动平台事件循环,并给应用一个运行环境。

最小结构大致是:

```rust
use gpui::*;

fn main() {
    gpui_platform::application().run(|cx: &mut App| {
        // 在这里打开窗口,挂载根视图
    });
}
```

这个入口做的事情可以按顺序理解:

```text
创建 Application
  ↓
进入 App 运行环境
  ↓
打开 Window
  ↓
创建根 View
  ↓
View 渲染成 Element 树
  ↓
GPUI 处理布局、事件和绘制
```

不同系统下,底层平台能力不同。macOS 使用 Metal 渲染;Linux / FreeBSD 通常需要 Wayland 或 X11 后端;Windows 使用 Win32 窗口和 DirectWrite 文字系统。`gpui_platform` 的作用,就是把这些平台差异包到统一入口后面。

## View:带状态的界面单元

GPUI 里最常见的界面单元是 View。

一个 View 是一个 Rust 对象。它保存自己的状态,并实现 `Render`,把当前状态渲染成界面节点。

例如:

```rust
use gpui::*;

struct HelloWorld {
    text: SharedString,
}

impl Render for HelloWorld {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .flex()
            .items_center()
            .justify_center()
            .child(format!("Hello, {}!", self.text))
    }
}
```

这和 React 组件有一点相似:组件根据状态返回 UI。但 GPUI 的组件不是 JavaScript 函数,而是 Rust 类型:

- 状态是 Rust struct;
- 渲染逻辑是 Rust 方法;
- UI 构造过程受 Rust 类型系统约束;
- 编译期能发现更多错误;
- 不需要额外的 JavaScript 运行时。

View 解决的是一个最基本的问题:应用状态如何变成屏幕上的界面。

## Element:真正被布局和绘制的节点

View 的 `render()` 不直接画像素,而是返回 Element。Element 是 GPUI 中构成界面的节点。

常见界面可以从 `div()` 开始组合:

```rust
div()
    .flex()
    .flex_col()
    .gap_3()
    .bg(rgb(0x505050))
    .text_color(rgb(0xffffff))
    .child("Hello GPUI")
```

这套写法很像 Tailwind:通过一串方法描述布局、间距、颜色、字体和子节点。

这里的关键不是语法像不像前端,而是开发方式变成了声明式:你描述“当前界面应该是什么样”,GPUI 再负责布局、事件命中和 GPU 绘制。

对于普通应用,View 加 Element 已经够用。对于编辑器文本区、超大列表、自定义画布这类性能敏感区域,GPUI 也允许开发者下沉到更底层的 Element,获得更细的布局和绘制控制。

## Entity:让复杂应用有地方放状态

桌面应用一旦复杂,就不能只靠几个局部变量管理状态。

以代码编辑器为例,它至少会有这些状态:

- 当前工作区;
- 文件树;
- 打开的文件;
- 编辑器 buffer;
- 光标和选择区;
- 搜索结果;
- 主题配置;
- 命令面板;
- 后台语言服务任务。

这些状态会被多个 View 使用,也会被异步任务、用户输入和系统事件修改。GPUI 用 Entity 管理这类应用状态。

可以先这样记:

```text
Entity = GPUI 管理的状态对象
View = 可以渲染的 Entity
Element = View 渲染出来的 UI 节点
```

Entity 的意义,是让状态成为框架能管理的对象,而不是散落在各个控件和回调里。这样应用越大,状态之间的关系越容易被组织起来。

## 开发一个跨平台 GPUI 应用的基本路线

一个简单 GPUI 应用可以按四步开始。

第一步,创建 Rust 项目:

```bash
cargo new my_gpui_app
cd my_gpui_app
```

第二步,加入依赖:

```toml
[dependencies]
gpui = "*"
gpui_platform = { version = "*", features = ["font-kit", "wayland", "x11"] }
```

这是一种方便起步的写法。真实项目里,更稳妥的做法是锁定明确版本。

平台 feature 可以按目标系统裁剪:

```text
macOS:Metal 渲染,文字通常需要 font-kit
Linux / FreeBSD:至少启用 wayland 或 x11
Windows:Win32 窗口 + DirectWrite 文字系统
```

第三步,写根 View:

```rust
use gpui::*;

struct AppView;

impl Render for AppView {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .size_full()
            .flex()
            .items_center()
            .justify_center()
            .child("Hello GPUI")
    }
}
```

第四步,打开窗口并挂载根 View:

```rust
fn main() {
    gpui_platform::application().run(|cx: &mut App| {
        cx.open_window(WindowOptions::default(), |_, cx| {
            cx.new(|_| AppView)
        }).unwrap();
    });
}
```

这就是一个 GPUI 应用的最小骨架。后续开发会围绕状态、视图、事件、快捷键、异步任务和数据持久化继续展开。

## 写 GPUI 应用时应该怎么思考

GPUI 的开发顺序可以从状态开始,而不是从控件开始。

比较自然的思路是:

```text
应用有哪些核心状态?
  ↓
哪些状态应该做成 Entity?
  ↓
哪些界面应该做成 View?
  ↓
View 如何渲染成 Element?
  ↓
用户输入如何修改状态?
  ↓
状态变化后哪些界面需要更新?
```

以 Todo 应用为例:

```text
TodoStore Entity
  保存 todo 列表

TodoListView
  展示列表

TodoInputView
  输入新任务

按钮点击 / 键盘事件
  修改 TodoStore

GPUI
  重新渲染相关 View
```

这种模型和传统 Widget 框架有明显差异。传统写法更容易变成“找到某个控件,然后手动改它的属性”。GPUI 更强调“状态改变后,界面由渲染函数重新描述”。

## 和 Electron 相比

Electron 的优势是生态。Web 技术栈成熟,招聘容易,组件库丰富,调试工具强大。很多桌面应用如果本来就是 Web 产品,Electron 是最快能跑起来的方案。

问题是 Electron 很重。它通常要带上 Chromium,资源占用和启动成本都不低。对于简单工具,这个成本可能可以接受;对于追求轻量、高响应和复杂交互的生产力工具,这个成本会越来越明显。

GPUI 的优势正好在另一边:它没有浏览器运行时,界面由 Rust 和 GPU 渲染路径直接支撑。代价是生态小,现成组件少,文档和案例远不如 Web 世界丰富。

可以简单比较:

```text
Electron:生态最强,开发最快,但运行时重
GPUI:更轻、更可控,但更依赖 Rust 能力和自建组件
```

## 和 Tauri 相比

Tauri 比 Electron 轻,因为它不捆绑 Chromium,而是使用系统 WebView。对于已有 Web 前端的团队,Tauri 是很自然的选择:前端继续写 HTML/CSS/JS,Rust 负责系统能力和后端逻辑。

但 Tauri 的 UI 仍然是 WebView。只要界面层还是网页,就仍然要接受 WebView 带来的边界。

GPUI 不走这条路。它的 UI 层本身就是 Rust 原生的。选择 GPUI,通常意味着你不打算复用 Web 前端,而是愿意为桌面应用建立一套更原生、更可控的 UI 架构。

所以两者的区别不是“谁更先进”,而是适用场景不同:

```text
Tauri:适合 Web 前端迁移桌面
GPUI:适合 Rust 原生复杂桌面工具
```

## 和 Qt / GTK 相比

Qt 和 GTK 是成熟的传统 GUI 框架。它们有大量控件、文档、案例和长期跨平台经验。需要稳定商业交付、成熟组件和老牌生态时,它们仍然很有优势。

它们的典型心智模型偏 Widget:

```text
窗口
  ↓
控件树
  ↓
信号 / 回调
  ↓
修改控件状态
```

GPUI 的模型更偏现代声明式 UI:

```text
状态
  ↓
View render
  ↓
Element 树
  ↓
布局和绘制
```

这种模型对复杂自定义界面更友好,尤其是编辑器、设计工具这类不满足于标准控件的应用。但它也要求团队接受较新的框架、更少的组件生态和更高的 Rust 使用门槛。

## GPUI 的真正优势

GPUI 的优势不在“开箱即用组件最多”,而在几个更底层的方向。

第一,它面向 GPU 渲染。复杂列表、文本编辑区、动画、多面板布局、高频刷新,都有更大的性能控制空间。

第二,它是 Rust 原生。状态、业务逻辑、异步任务和 UI 可以被同一种语言和类型系统组织起来,减少前后端边界。

第三,它有真实复杂产品验证。Zed 编辑器本身就是 GPUI 的核心应用场景,说明它的目标不是简单按钮和表单,而是高交互生产力软件。

第四,它同时提供高层和底层入口。普通界面可以用 View 声明式描述,性能敏感区域可以通过更底层的 Element 自定义。

## 什么时候适合选择 GPUI

适合考虑 GPUI 的场景包括:

- 编辑器;
- IDE;
- AI 桌面工具;
- 设计工具;
- 高性能开发者工具;
- 复杂多窗口生产力应用;
- 团队已经熟悉 Rust,愿意接受较新的 UI 框架。

不太适合的场景包括:

- 主要是表单和后台管理页面;
- 已经有完整 Web 前端,只想快速包装成桌面应用;
- 需要大量现成商业组件;
- 团队主要是前端,Rust 经验不足;
- 项目短期交付优先,不能承受 pre-1.0 框架变化。

GPUI 仍在快速发展中,还没有进入 1.0 阶段,版本之间可能出现 breaking changes。选择它之前,需要接受这个现实。

## 总结

GPUI 代表的是一条明确的桌面应用路线:

```text
不用浏览器做 UI 运行时
不用传统 Widget 作为主要抽象
用 Rust 管状态和交互
用 View / Element 描述界面
用 GPU 完成渲染
```

如果目标是快速把 Web 应用搬到桌面,Electron 或 Tauri 更合适。如果目标是构建一个长期维护、性能敏感、交互复杂的 Rust 原生桌面软件,GPUI 的路线就很有吸引力。

一句话概括:

> GPUI 是一个 Rust 原生、GPU 加速、面向复杂生产力工具的跨平台桌面 UI 框架。它牺牲了一部分生态成熟度,换来更统一的 Rust 开发模型和更强的界面性能控制。