Rust 学习笔记 12:常用集合 (Collections)

Rust 学习笔记 12:常用集合 (Collections) “Data structures are the backbone of any program.” Rust 标准库提供了一系列非常实用的集合数据结构。和数组/元组不同,它们的数据存储在堆 (Heap) 上,这意味着长度可以动态变化。 如果你有 Go 语言背景,可以这样类比: Vec<T> ≈ Slice []T String ≈ string (但在 Rust 中它是可变的) HashMap<K, V> ≈ Map map[K]V 1. Vector (动态数组) Vector 是最常用的集合。 1let mut v: Vec<i32> = Vec::new(); 2v.push(1); 3v.push(2); 4v.push(3); 或者使用 vec! 宏: 1let v = vec![1, 2, 3]; 访问元素: 有两种方式,一种是索引 &v[2](越界会 Panic),一种是 v.get(2)(返回 Option<&T>)。在 Rust 中,推荐尽可能用 get 来安全处理越界。 借用规则陷阱: 当你持有一个元素的引用时,不能往 Vector 里 push 新元素! 原因:push 可能会导致 vector 扩容(重新分配内存),如果你手里还攥着旧内存的引用,那就是悬垂指针了。Rust 编译器会死死盯着你。 ...

2025-02-18 · 2 min · 253 words · 老墨

Rust 学习笔记 07:Slice 类型

Rust 学习笔记 07:Slice 类型 “Slice and dice your data safely.” 在 Go 语言中,Slice (切片) 是一个非常核心的概念,它底层是一个结构体 (ptr, len, cap)。 在 Rust 中,Slice 也是类似的,但它有一个本质的区别:Rust 的 Slice 是一种引用(Borrowed Type)。 它不拥有数据,它只是借用了数据的一部分。 1. 字符串切片 (String Slices) 假设我们有一个字符串 s: 1let s = String::from("hello world"); 我们可以创建一个切片,指向它的一部分: 1let hello = &s[0..5]; // 指向 0,1,2,3,4 2let world = &s[6..11]; 注意那个 & 符号,说明 hello 是一个引用。 它的类型是 &str(读作 “string slice”)。它内部包含两个字段: 指向数据的指针 (ptr) 切片的长度 (len) 这和 Go 的 Slice 结构几乎一样,只是少了容量 (cap),且必须依附于原字符串存在。 2. 切片与所有权 既然 Slice 是引用,它就受借用规则的约束。 ...

2024-09-08 · 2 min · 217 words · 老墨

(译)Rob Pike 带你深入理解 Go 字符串:字节、符文与 Unicode 编码

大家好,我是极客老墨! 今天这篇文章是早期老墨翻译自 Golang 官方博客文章,有一定的深度,写的非常好,读完你会对字符串的设计和底层原理有一个明确的认识,建议收藏后细细品味。 原文地址: https://go.dev/blog/strings, Rob Pike 简介 这篇文章讨论了 Go 中的字符串。起初,字符串对于一篇博文来说似乎太简单了,但要很好地使用它们,不仅需要了解它们的工作原理,还需要了解字节、字符和符文之间的区别,Unicode 和 UTF- 8、字符串和字符串字面量的区别,以及其他更细微的区别。 处理该话题的一种方法首先是回答这个问题:“当我在位置 n 检索 Go 字符串时,为什么我没有得到第 n 个字符?” 正如您将看到的,这个问题引导我们了解有关文本在现代世界中如何工作的许多细节。 什么是字符串? 让我们从一些基础知识开始。 在 Go 中,字符串实际上是只读的字节切片。如果您完全不确定字节切片是什么或它是如何工作的,请阅读 数组、切片和字符串 一文。 重要的是首先要明确一个字符串包含_任意_多个字节,不论字符串是否包含 Unicode 文本、UTF-8 文本或任何其他预定义格式。就字符串的内容而言,它完全等价于一个字节切片([]byte)。 下边是一个字符串(稍后详述),它使用 \xNN 符号来定义一个字符串常量,其中包含一些特殊的字节值(字节的取值范围从十六进制值 00 到 FF)。 1const sample = "\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98" 打印字符串 由于上边我们的示例字符串 sample 中的某些字节不是有效的 ASCII,甚至不是有效的 UTF-8,所以直接打印字符串会产生奇怪的输出。简单的打印语句如下: 1fmt.Println(sample) 产生这种乱码(输出可能因环境而异)输出: 1��=� ⌘ 为了找出 sample 字符串底层到底是什么,我们需要把它拆开检查一下。有几种方法可以做到这一点。最明显的是循环其内容并单独提取字节,如以下for循环所示: 1for i := 0; i < len(sample); i++ { 2 fmt.Printf("%x ", sample[i]) // 输出为十六进制格式 3} 正如前文所述,索引字符串访问的是单个字节,而不是单个字符,我们将在下面详细阐述该主题。现在,让我们只使用字节,这是逐字节循环的十六进制输出: ...

2023-03-25 · 4 min · 714 words · 老墨

(译)Go创造者Rob Pike带你深入了解数组、切片和字符串 底层的 “append” 原理

大家好,我是极客老墨! 今天这篇文章是早期老墨翻译自 Golang 官方博客文章,有一定的深度,非常经典,读完你会深入了解数组、切片和字符串的 Append 原理,建议收藏后细细品味。 原文地址: https://go.dev/blog/slices,Rob Pike 介绍 过程编程语言最常见的特征之一是数组的概念。数组看起来很简单,但在将它们添加到语言时必须回答许多问题,例如: 固定尺寸还是可变尺寸? 大小是类型的一部分吗? 多维数组是什么样的? 空数组有意义吗? 这些问题的答案会影响数组是否只是语言的一个特性还是其设计的核心部分。 在 Go 的早期开发中,在设计感觉正确之前,花了大约一年的时间来确定这些问题的答案。关键步骤是引入 slices(切片),它建立在固定大小的 array (数组)之上,以提供灵活、可扩展的数据结构。然而,直到今天,刚接触 Go 的程序员经常对切片的工作方式感到困惑,也许是因为其他语言的经验影响了他们的思维。 在这篇文章中,我们将尝试消除混淆。我们将通过构建片段来解释 append 内置函数是如何工作的,以及为什么它会以这种方式工作。 数组 数组是 Go 中的一个重要构建块,但就像建筑物的基础一样,它们通常隐藏在更可见的组件之下。在我们继续讨论更有趣、更强大、更突出的切片概念之前,我们必须简单地讨论一下它们。 数组在 Go 程序中并不常见,因为数组的大小是其类型的一部分,这限制了它的表达能力。 以下代码: 1var buffer [256]byte 声明了一个数组变量 buffer ,[256]byte 表示它持有的数据类型为 byte,长度为 256。如果想声明 512 个字节的数组可以这样: [512]byte。 与数组关联的数据就是 数组中的元素。上边声明的数组缓冲区在内存中看起来像这样: 1buffer: byte byte byte ... 256 times ... byte byte byte 也就是说,该变量只保存 256 个字节的数据,仅此而已。我们可以使用熟悉的索引语法 buffer[0]、buffer[1] 到 buffer[255] 来访问它的元素。(索引范围 0 到 255 涵盖 256 个元素)尝试使用超出此范围的索引值访问 buffer 会使程序崩溃。 ...

2023-02-09 · 8 min · 1616 words · 老墨