Sway 字符串(Strings)完整指南:双引号规则、UTF-8 编码与不可索引限制
2026/9/12 16:19:33 网站建设 项目流程

Sway 字符串(Strings)完整指南:双引号规则、UTF-8 编码与不可索引限制

【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway

本篇技术指南聚焦 Sway 语言中字符串(String)这一内置类型的核心规则与已知使用限制,包括必须使用双引号声明、字符串为 UTF-8 编码且不可按索引访问等关键约束。读者读完本文后,将能够正确声明 Sway 字符串、规避常见编译错误,并理解其背后的类型系统与标准库实现原理,为编写智能合约中的字符串处理代码打下基础。

概述:Sway 字符串的两大核心约束

Sway 是 Fuel 生态中用于编写智能合约的领域特定语言。在 Sway 中,字符串是最常用的内置类型之一,但它与许多主流语言(如 Rust、Python)中的字符串在使用习惯上存在明显差异。根据 Sway 官方参考文档 workarounds/strings.md,Sway 字符串主要有两条必须牢记的规则:

  1. 字符串必须使用双引号"声明,单引号'不能被使用。尝试用单引号定义字符串会直接导致编译错误。
  2. 字符串采用 UTF-8 编码,因此不能按索引(下标)访问。试图通过str[index]获取某个位置的字符同样会触发编译错误。

这两条规则看似简单,却深刻影响了智能合约中字符串的声明与操作方式。下文将逐一展开,并结合仓库源码与标准库实现进行深入剖析。

规则一:字符串必须使用双引号声明

在 Sway 中,字符串字面量(string literal)只能使用双引号"包裹。官方文档给出的规则为:

Sway strings are declared using double-quotes". Single quotes'cannot be used. Attempting to define a string with single-quotes will result in an error.

官方参考文档通过{{#include}}机制引入了仓库中的实际示例代码 string_issue/src/lib.sw,用于演示错误用法:

// Will error if uncommented // let fuel = 'fuel';

也就是说,以下写法在 Sway 中是非法的:

// 错误:单引号不能用于声明字符串 let fuel = 'fuel';

正确的声明方式必须使用双引号:

let fuel = "fuel";

为什么单引号不可用?

从 Sway 的类型系统设计来看,单引号在语言中另有用途——它用于标记泛型类型参数(generic type parameters),例如abi MyContract<T, U>中的TU等占位符。为了避免语法歧义并保持语言内部一致性,字符串字面量被明确限定为双引号形式。这一设计决策使得编译器在词法分析阶段就能快速、无歧义地识别字符串字面量与泛型标识符,从而简化解析流程。

官方为验证这一限制,专门创建了一个名为string_issue的库项目(见 string_issue/Forc.toml),其entry指向lib.sw,并依赖仓库中的sway-lib-std标准库。示例代码中,错误写法均被注释保留,读者可自行取消注释并通过forc build观察编译错误输出。

规则二:字符串为 UTF-8 编码且不可索引

官方文档给出的第二条规则为:

Strings are UTF-8 encoded therefore they cannot be indexed.

仓库中的演示代码同样以注释形式展示了错误用法:

let fuel = "fuel"; // Will error if uncommented // let f = fuel[0];

也就是说,试图通过下标访问字符串中的字符(如fuel[0])会引发编译错误。

不可索引的深层原因:UTF-8 变长编码

Sway 字符串默认采用UTF-8 编码(这一点在参考文档 language/built-ins/string.md 中也有明确说明)。UTF-8 是一种变长编码:ASCII 字符占用 1 个字节,而其他 Unicode 字符可能占用 2、3 甚至 4 个字节。因此,字符串中的第n个"字符"并不一定对应内存中的第n个字节,无法通过简单的固定步长索引来定位某个字符。

如果允许str[index]这样的操作,编译器无法在编译期确定该索引指向的是一个完整字符还是字符中间的某个字节,也就无法保证返回值的正确性与安全性。因此 Sway 语言直接禁止了对字符串的索引操作,从而在类型层面规避了这一类潜在的错误。

从源码看 Sway 字符串的内存表示

Sway 标准库 str.sw 的实现揭示了字符串在底层的表示方式。str在底层被表示为(raw_ptr, u64)的元组——即"指向字符串起始位置的原始指针 + 字符串字节长度":

impl str { /// Return a `raw_ptr` to the beginning of the string slice. pub fn as_ptr(self) -> raw_ptr { let (ptr, _) = __transmute::<str, (raw_ptr, u64)>(self); ptr } /// Return the length of the string slice in bytes. pub fn len(self) -> u64 { let (_, len) = __transmute::<str, (raw_ptr, u64)>(self); len } }

从这段源码可以推断:str本身并不携带"字符"级的信息,只有字节指针与字节长度。这也从实现层面印证了不可索引的必然性——既然底层只保存字节数据而没有字符边界信息,按字符索引自然无从谈起。

字符串的长度:固定长度类型

在深入了解 Sway 字符串之前,还需要明确一个关键前提:Sway 的str固定长度类型。参考文档 language/built-ins/string.md 明确指出其含义:

  • 字符串在运行期间不能被增长(grow)或缩小(shrink);
  • 字符串的内容必须满足其声明长度——要么由合法内容恰好占满整个长度,要么通过填充(padding)补齐;
  • 原因在于编译器必须知道类型的尺寸,而长度本身就是类型的一部分

字符串通过双引号创建时,其长度被永久固定,即使将变量标记为可变(mut)也无法改变:

let fuel = "fuel"; // str[4],长度为 4

由于长度属于类型的一部分,Sway 中存在str[N]这种带编译期长度的字符串数组类型(string array)。固定长度特性与不可索引规则共同构成了 Sway 字符串"少即是多"的设计哲学:类型信息在编译期完全确定,为智能合约的安全性与可验证性提供了保障。

字符串数组:str[N]与可变长度str的转换

既然长度是类型的一部分,Sway 提供了字符串数组str[N]与字符串切片str之间的转换能力。仓库示例 examples/converting_types/src/strings.sw 演示了完整的转换流程:

pub fn convert_str_to_str_array() { let fuel_str: str = "fuel"; let fuel_str_array: str[4] = fuel_str.try_as_str_array().unwrap(); } pub fn convert_str_array_to_str() { let fuel_str_array: str[4] = __to_str_array("fuel"); let fuel_str: str = from_str_array(fuel_str_array); }

转换时,编译器要求str[N]的编译期长度N与实际字符串长度完全一致(如str[4]对应"fuel"),否则会在编译期报错,这正是"长度属于类型"的直接体现。

标准库 str.sw 中from_str_array的实现展示了转换底层机制:

pub fn from_str_array<S>(s: S) -> str { __assert_is_str_array::<S>(); let str_size = __size_of_str_array::<S>(); let src = __addr_of(s); let ptr = asm(size: __size_of::<S>(), dest, src: src) { aloc size; move dest hp; mcp dest src size; dest: raw_ptr }; __transmute::<(raw_ptr, u64), str>((ptr, str_size)) }

从这段代码可以看出:from_str_array通过编译器内建函数获取字符串数组的尺寸与地址,在内联汇编中借助aloc分配堆内存、mcp复制字节内容,最终以(raw_ptr, u64)的形式transmutestr。整个转换过程完全发生在编译期与运行时的边界上,进一步印证了字符串的类型安全设计。

实战:在智能合约中正确使用字符串

综合以上规则,在实际编写 Sway 代码时应遵循以下最佳实践:

1. 始终使用双引号声明字符串

contract; // 正确 let network = "fuel"; // 错误:单引号会导致编译错误 // let network = 'fuel';

2. 不要对字符串使用索引访问

// 错误:字符串不可索引 // let first = network[0];

如需处理字符串中的单个字符,应通过标准库提供的转换手段将字符串转为字节数据(如std::string::StringBytes)后再按字节处理。

3. 利用固定长度特性进行编译期校验

字符串数组str[N]的声明让编译器在编译期即可校验长度,适合对固定格式数据(如合约地址格式、固定长度的标识符)进行强类型约束。

4. 需要动态字符串时使用标准库String

当智能合约确实需要可增长的字符串时,可以使用标准库 string.sw 中定义的String类型。它是一个UTF-8 编码、可增长的字符串,拥有自己的缓冲区,并提供from_ascii_strlenpush相关能力、as_strclearcapacity等 API。String底层基于Bytes实现,并实现了AbiEncode/AbiDecodeHashPartialEqClone等 trait,可直接用于跨合约调用与存储场景。

常见编译错误速查

以下表格总结了本文涉及的两种错误场景,便于快速排查:

错误写法错误原因正确写法
let fuel = 'fuel';单引号不能声明字符串let fuel = "fuel";
let f = fuel[0];字符串为 UTF-8 编码,不可索引通过标准库转换后按字节处理

这两类错误在 string_issue/src/lib.sw 中均有注释演示,读者可结合forc build亲自验证。

小结

Sway 字符串的核心规则可以浓缩为两点:双引号声明、UTF-8 编码不可索引。前者源于语言语法设计(单引号保留给泛型参数),后者源于 UTF-8 变长编码与底层(raw_ptr, u64)的字节级表示。加上字符串固定长度、"长度属于类型"的特性,Sway 在字符串处理上展现出强烈的编译期安全倾向。理解这些约束,是写出正确、高效的 Sway 智能合约代码的必修课。

【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询