mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
212 lines
19 KiB
HTML
212 lines
19 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>vector - Rust 程序设计语言 简体中文版</title>
|
||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||
<meta name="description" content="Rust 程序设计语言 简体中文版">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
||
<base href="">
|
||
|
||
<link rel="stylesheet" href="book.css">
|
||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
||
|
||
<link rel="shortcut icon" href="favicon.png">
|
||
|
||
<!-- Font Awesome -->
|
||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||
|
||
<link rel="stylesheet" href="highlight.css">
|
||
<link rel="stylesheet" href="tomorrow-night.css">
|
||
|
||
<!-- MathJax -->
|
||
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
|
||
<!-- Fetch JQuery from CDN but have a local fallback -->
|
||
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
||
<script>
|
||
if (typeof jQuery == 'undefined') {
|
||
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
|
||
}
|
||
</script>
|
||
</head>
|
||
<body class="light">
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script type="text/javascript">
|
||
var theme = localStorage.getItem('theme');
|
||
if (theme == null) { theme = 'light'; }
|
||
$('body').removeClass().addClass(theme);
|
||
</script>
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script type="text/javascript">
|
||
var sidebar = localStorage.getItem('sidebar');
|
||
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
|
||
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
|
||
</script>
|
||
|
||
<div id="sidebar" class="sidebar">
|
||
<ul class="chapter"><li><a href="ch01-00-introduction.html"><strong>1.</strong> 介绍</a></li><li><ul class="section"><li><a href="ch01-01-installation.html"><strong>1.1.</strong> 安装</a></li><li><a href="ch01-02-hello-world.html"><strong>1.2.</strong> Hello, World!</a></li></ul></li><li><a href="ch02-00-guessing-game-tutorial.html"><strong>2.</strong> 猜猜看教程</a></li><li><a href="ch03-00-common-programming-concepts.html"><strong>3.</strong> 通用编程概念</a></li><li><ul class="section"><li><a href="ch03-01-variables-and-mutability.html"><strong>3.1.</strong> 变量和可变性</a></li><li><a href="ch03-02-data-types.html"><strong>3.2.</strong> 数据类型</a></li><li><a href="ch03-03-how-functions-work.html"><strong>3.3.</strong> 函数如何工作</a></li><li><a href="ch03-04-comments.html"><strong>3.4.</strong> 注释</a></li><li><a href="ch03-05-control-flow.html"><strong>3.5.</strong> 控制流</a></li></ul></li><li><a href="ch04-00-understanding-ownership.html"><strong>4.</strong> 认识所有权</a></li><li><ul class="section"><li><a href="ch04-01-what-is-ownership.html"><strong>4.1.</strong> 什么是所有权</a></li><li><a href="ch04-02-references-and-borrowing.html"><strong>4.2.</strong> 引用 & 借用</a></li><li><a href="ch04-03-slices.html"><strong>4.3.</strong> Slices</a></li></ul></li><li><a href="ch05-00-structs.html"><strong>5.</strong> 结构体</a></li><li><ul class="section"><li><a href="ch05-01-method-syntax.html"><strong>5.1.</strong> 方法语法</a></li></ul></li><li><a href="ch06-00-enums.html"><strong>6.</strong> 枚举和模式匹配</a></li><li><ul class="section"><li><a href="ch06-01-defining-an-enum.html"><strong>6.1.</strong> 定义枚举</a></li><li><a href="ch06-02-match.html"><strong>6.2.</strong> <code>match</code>控制流运算符</a></li><li><a href="ch06-03-if-let.html"><strong>6.3.</strong> <code>if let</code>简单控制流</a></li></ul></li><li><a href="ch07-00-modules.html"><strong>7.</strong> 模块</a></li><li><ul class="section"><li><a href="ch07-01-mod-and-the-filesystem.html"><strong>7.1.</strong> <code>mod</code>和文件系统</a></li><li><a href="ch07-02-controlling-visibility-with-pub.html"><strong>7.2.</strong> 使用<code>pub</code>控制可见性</a></li><li><a href="ch07-03-importing-names-with-use.html"><strong>7.3.</strong> 使用<code>use</code>导入命名</a></li></ul></li><li><a href="ch08-00-common-collections.html"><strong>8.</strong> 通用集合类型</a></li><li><ul class="section"><li><a href="ch08-01-vectors.html" class="active"><strong>8.1.</strong> vector</a></li><li><a href="ch08-02-strings.html"><strong>8.2.</strong> 字符串</a></li><li><a href="ch08-03-hash-maps.html"><strong>8.3.</strong> 哈希 map</a></li></ul></li><li><a href="ch09-00-error-handling.html"><strong>9.</strong> 错误处理</a></li><li><ul class="section"><li><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong>9.1.</strong> <code>panic!</code>与不可恢复的错误</a></li><li><a href="ch09-02-recoverable-errors-with-result.html"><strong>9.2.</strong> <code>Result</code>与可恢复的错误</a></li><li><a href="ch09-03-to-panic-or-not-to-panic.html"><strong>9.3.</strong> <code>panic!</code>还是不<code>panic!</code></a></li></ul></li><li><a href="ch10-00-generics.html"><strong>10.</strong> 泛型、trait 和生命周期</a></li><li><ul class="section"><li><a href="ch10-01-syntax.html"><strong>10.1.</strong> 泛型数据类型</a></li><li><a href="ch10-02-traits.html"><strong>10.2.</strong> trait:定义共享的行为</a></li><li><a href="ch10-03-lifetime-syntax.html"><strong>10.3.</strong> 生命周期与引用有效性</a></li></ul></li><li><a href="ch11-00-testing.html"><strong>11.</strong> 测试</a></li><li><ul class="section"><li><a href="ch11-01-writing-tests.html"><strong>11.1.</strong> 编写测试</a></li><li><a href="ch11-02-running-tests.html"><strong>11.2.</strong> 运行测试</a></li><li><a href="ch11-03-test-organization.html"><strong>11.3.</strong> 测试的组织结构</a></li></ul></li><li><a href="ch12-00-an-io-project.html"><strong>12.</strong> 一个 I/O 项目</a></li><li><ul class="section"><li><a href="ch12-01-accepting-command-line-arguments.html"><strong>12.1.</strong> 接受命令行参数</a></li><li><a href="ch12-02-reading-a-file.html"><strong>12.2.</strong> 读取文件</a></li><li><a href="ch12-03-improving-error-handling-and-modularity.html"><strong>12.3.</strong> 增强错误处理和模块化</a></li><li><a href="ch12-04-testing-the-librarys-functionality.html"><strong>12.4.</strong> 测试库的功能</a></li><li><a href="ch12-05-working-with-environment-variables.html"><strong>12.5.</strong> 处理环境变量</a></li><li><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong>12.6.</strong> 输出到<code>stderr</code>而不是<code>stdout</code></a></li></ul></li></ul>
|
||
</div>
|
||
|
||
<div id="page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<div id="menu-bar" class="menu-bar">
|
||
<div class="left-buttons">
|
||
<i id="sidebar-toggle" class="fa fa-bars"></i>
|
||
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
||
</div>
|
||
|
||
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
|
||
|
||
<div class="right-buttons">
|
||
<i id="print-button" class="fa fa-print" title="Print this book"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="content" class="content">
|
||
<a class="header" href="#vector" name="vector"><h2>vector</h2></a>
|
||
<blockquote>
|
||
<p><a href="https://github.com/rust-lang/book/blob/master/src/ch08-01-vectors.md">ch08-01-vectors.md</a>
|
||
<br>
|
||
commit 0d229cc5a3da341196e15a6761735b2952281569</p>
|
||
</blockquote>
|
||
<p>我们要讲到的第一个类型是<code>Vec<T></code>,也被称为 <em>vector</em>。vector 允许我们在一个单独的数据结构中储存多于一个值,它在内存中彼此相邻的排列所有的值。vector 只能储存相同类型的值。他们在拥有一系列的场景下非常实用,例如文件中的文本行或是购物车中商品的价格。</p>
|
||
<a class="header" href="#新建-vector" name="新建-vector"><h3>新建 vector</h3></a>
|
||
<p>为了创建一个新的,空的 vector,可以调用<code>Vec::new</code>函数:</p>
|
||
<pre><code class="language-rust">let v: Vec<i32> = Vec::new();
|
||
</code></pre>
|
||
<p>注意这里我们增加了一个类型注解。因为没有向这个 vector 中插入任何值,Rust 并不知道我们想要储存什么类型的元素。这是一个非常重要的点。vector 是同质的(homogeneous):他们可以储存很多值,不过这些值必须都是相同类型的。vector 是用泛型实现的,第十章会涉及到如何对你自己的类型使用他们。现在,所有你需要知道的就是<code>Vec</code>是一个由标准库提供的类型,它可以存放任何类型,而当<code>Vec</code>存放某个特定类型时,那个类型位于尖括号中。这里我们告诉 Rust <code>v</code>这个<code>Vec</code>将存放<code>i32</code>类型的元素。</p>
|
||
<p>在实际的代码中,一旦插入值 Rust 就可以推断出想要存放的类型,所以你很少会需要这些类型注解。更常见的做法是使用初始值来创建一个<code>Vec</code>,而且为了方便 Rust 提供了<code>vec!</code>宏。这个宏会根据我们提供的值来创建一个新的<code>Vec</code>。如下代码会新建一个拥有值<code>1</code>、<code>2</code>和<code>3</code>的<code>Vec<i32></code>:</p>
|
||
<pre><code class="language-rust">let v = vec![1, 2, 3];
|
||
</code></pre>
|
||
<p>因为我们提供了<code>i32</code>类型的初始值,Rust 可以推断出<code>v</code>的类型是<code>Vec<i32></code>,因此类型注解就不是必须的。接下来让我们看看如何修改一个 vector。</p>
|
||
<a class="header" href="#更新-vector" name="更新-vector"><h3>更新 vector</h3></a>
|
||
<p>对于新建一个 vector 并向其增加元素,可以使用<code>push</code>方法:</p>
|
||
<pre><code class="language-rust">let mut v = Vec::new();
|
||
|
||
v.push(5);
|
||
v.push(6);
|
||
v.push(7);
|
||
v.push(8);
|
||
</code></pre>
|
||
<p>如第三章中讨论的任何变量一样,如果想要能够改变它的值,必须使用<code>mut</code>关键字使其可变。放入其中的所有值都是<code>i32</code>类型的,而且 Rust 也根据数据如此判断,所以不需要<code>Vec<i32></code>注解。</p>
|
||
<a class="header" href="#丢弃-vector-时也会丢弃其所有元素" name="丢弃-vector-时也会丢弃其所有元素"><h3>丢弃 vector 时也会丢弃其所有元素</h3></a>
|
||
<p>类似于任何其他的<code>struct</code>,vector 在其离开作用域时会被释放:</p>
|
||
<pre><code class="language-rust">{
|
||
let v = vec![1, 2, 3, 4];
|
||
|
||
// do stuff with v
|
||
|
||
} // <- v goes out of scope and is freed here
|
||
</code></pre>
|
||
<p>当 vector 被丢弃时,所有其内容也会被丢弃,这意味着这里它包含的整数将被清理。这可能看起来非常直观,不过一旦开始使用 vector 元素的引用情况就变得有些复杂了。下面让我们处理这种情况!</p>
|
||
<a class="header" href="#读取-vector-的元素" name="读取-vector-的元素"><h3>读取 vector 的元素</h3></a>
|
||
<p>现在你知道如何创建、更新和销毁 vector 了,接下来的一步最好了解一下如何读取他们的内容。有两种方法引用 vector 中储存的值。为了更加清楚的说明这个例子,我们标注这些函数返回的值的类型。</p>
|
||
<p>这个例子展示了访问 vector 中一个值的两种方式,索引语法或者<code>get</code>方法:</p>
|
||
<pre><code class="language-rust">let v = vec![1, 2, 3, 4, 5];
|
||
|
||
let third: &i32 = &v[2];
|
||
let third: Option<&i32> = v.get(2);
|
||
</code></pre>
|
||
<p>这里有一些需要注意的地方。首先,我们使用索引值<code>2</code>来获取第三个元素,索引是从 0 开始的。其次,这两个不同的获取第三个元素的方式分别为:使用<code>&</code>和<code>[]</code>返回一个引用;或者使用<code>get</code>方法以索引作为参数来返回一个<code>Option<&T></code>。</p>
|
||
<p>Rust 有两个引用元素的方法的原因是程序可以选择如何处理当索引值在 vector 中没有对应值的情况。例如如下情况,如果有一个有五个元素的 vector 接着尝试访问索引为 100 的元素,程序该如何处理:</p>
|
||
<pre><code class="language-rust,should_panic">let v = vec![1, 2, 3, 4, 5];
|
||
|
||
let does_not_exist = &v[100];
|
||
let does_not_exist = v.get(100);
|
||
</code></pre>
|
||
<p>当运行这段代码,你会发现对于第一个<code>[]</code>方法,当引用一个不存在的元素时 Rust 会造成<code>panic!</code>。这个方法更适合当程序认为尝试访问超过 vector 结尾的元素是一个严重错误的情况,这时应该使程序崩溃。</p>
|
||
<p>当<code>get</code>方法被传递了一个数组外的索引时,它不会 panic 而是返回<code>None</code>。当偶尔出现超过 vector 范围的访问属于正常情况的时候可以考虑使用它。接着你的代码可以有处理<code>Some(&element)</code>或<code>None</code>的逻辑,如第六章讨论的那样。例如,索引可能来源于用户输入的数字。如果他们不慎输入了一个过大的数字那么程序就会得到<code>None</code>值,你可以告诉用户<code>Vec</code>当前元素的数量并再请求他们输入一个有效的值。这就比因为输入错误而使程序崩溃要友好的多!</p>
|
||
<a class="header" href="#无效引用" name="无效引用"><h4>无效引用</h4></a>
|
||
<p>一旦程序获取了一个有效的引用,借用检查器将会执行第四章讲到的所有权和借用规则来确保 vector 内容的这个引用和任何其他引用保持有效。回忆一下不能在相同作用域中同时存在可变和不可变引用的规则。这个规则适用于这个例子,当我们获取了 vector 的第一个元素的不可变引用并尝试在 vector 末尾增加一个元素的时候:</p>
|
||
<pre><code class="language-rust,ignore">let mut v = vec![1, 2, 3, 4, 5];
|
||
|
||
let first = &v[0];
|
||
|
||
v.push(6);
|
||
</code></pre>
|
||
<p>编译会给出这个错误:</p>
|
||
<pre><code>error[E0502]: cannot borrow `v` as mutable because it is also borrowed as
|
||
immutable
|
||
|
|
||
4 | let first = &v[0];
|
||
| - immutable borrow occurs here
|
||
5 |
|
||
6 | v.push(6);
|
||
| ^ mutable borrow occurs here
|
||
7 | }
|
||
| - immutable borrow ends here
|
||
</code></pre>
|
||
<p>这些代码看起来应该能够运行:为什么第一个元素的引用会关心 vector 结尾的变化?不能这么做的原因是由于 vector 的工作方式。在 vector 的结尾增加新元素是,在没有足够空间将所有所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中。这时,第一个元素的引用就指向了被释放的内存。借用规则阻止程序陷入这种状况。</p>
|
||
<blockquote>
|
||
<p>注意:关于更多内容,查看 Nomicon <em>https://doc.rust-lang.org/stable/nomicon/vec.html</em></p>
|
||
</blockquote>
|
||
<a class="header" href="#使用枚举来储存多种类型" name="使用枚举来储存多种类型"><h3>使用枚举来储存多种类型</h3></a>
|
||
<p>在本章的开始,我们提到 vector 只能储存相同类型的值。这是很不方便的;绝对会有需要储存一系列不同类型的值的用例。幸运的是,枚举的成员都被定义为相同的枚举类型,所以当需要在 vector 中储存不同类型值时,我们可以定义并使用一个枚举!</p>
|
||
<p>例如,假如我们想要从电子表格的一行中获取值,而这一行的有些列包含数字,有些包含浮点值,还有些是字符串。我们可以定义一个枚举,其成员会存放这些不同类型的值,同时所有这些枚举成员都会被当作相同类型,那个枚举的类型。接着可以创建一个储存枚举值的 vector,这样最终就能够储存不同类型的值了:</p>
|
||
<pre><code class="language-rust">enum SpreadsheetCell {
|
||
Int(i32),
|
||
Float(f64),
|
||
Text(String),
|
||
}
|
||
|
||
let row = vec![
|
||
SpreadsheetCell::Int(3),
|
||
SpreadsheetCell::Text(String::from("blue")),
|
||
SpreadsheetCell::Float(10.12),
|
||
];
|
||
</code></pre>
|
||
<p>Rust 在编译时就必须准确的知道 vector 中类型的原因是它需要知道储存每个元素到底需要多少内存。第二个优点是可以准确的知道这个 vector 中允许什么类型。如果 Rust 允许 vector 存放任意类型,那么当对 vector 元素执行操作时一个或多个类型的值就有可能会造成错误。使用枚举外加<code>match</code>意味着 Rust 能在编译时就保证总是会处理所有可能的情况,正如第六章讲到的那样。</p>
|
||
<p>如果在编写程序时不能确切无遗的知道运行时会储存进 vector 的所有类型,枚举技术就行不通了。相反,你可以使用 trait 对象,第十七章会讲到它。</p>
|
||
<p>现在我们了解了一些使用 vector 的最常见的方式,请一定去看看标准库中<code>Vec</code>定义的很多其他实用方法的 API 文档。例如,除了<code>push</code>之外还有一个<code>pop</code>方法,它会移除并返回 vector 的最后一个元素。让我们继续下一个集合类型:<code>String</code>!</p>
|
||
|
||
</div>
|
||
|
||
<!-- Mobile navigation buttons -->
|
||
|
||
<a href="ch08-00-common-collections.html" class="mobile-nav-chapters previous">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
|
||
|
||
<a href="ch08-02-strings.html" class="mobile-nav-chapters next">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
<a href="ch08-00-common-collections.html" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
|
||
|
||
<a href="ch08-02-strings.html" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
<!-- Local fallback for Font Awesome -->
|
||
<script>
|
||
if ($(".fa").css("font-family") !== "FontAwesome") {
|
||
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
|
||
}
|
||
</script>
|
||
|
||
<!-- Livereload script (if served using the cli tool) -->
|
||
|
||
|
||
<script src="highlight.js"></script>
|
||
<script src="book.js"></script>
|
||
</body>
|
||
</html>
|