|
|
<!DOCTYPE HTML>
|
|
|
<html lang="zh-CN" class="light" dir="ltr">
|
|
|
<head>
|
|
|
<!-- Book generated using mdBook -->
|
|
|
<meta charset="UTF-8">
|
|
|
<title>字符串与切片 - Rust语言圣经(Rust Course)</title>
|
|
|
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
|
|
<meta name="description" content="">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
<meta name="theme-color" content="#ffffff">
|
|
|
|
|
|
<link rel="icon" href="../../favicon.svg">
|
|
|
<link rel="shortcut icon" href="../../favicon.png">
|
|
|
<link rel="stylesheet" href="../../css/variables.css">
|
|
|
<link rel="stylesheet" href="../../css/general.css">
|
|
|
<link rel="stylesheet" href="../../css/chrome.css">
|
|
|
<link rel="stylesheet" href="../../css/print.css" media="print">
|
|
|
|
|
|
<!-- Fonts -->
|
|
|
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
|
|
<link rel="stylesheet" href="../../fonts/fonts.css">
|
|
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
|
<link rel="stylesheet" href="../../highlight.css">
|
|
|
<link rel="stylesheet" href="../../tomorrow-night.css">
|
|
|
<link rel="stylesheet" href="../../ayu-highlight.css">
|
|
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
<link rel="stylesheet" href="../../theme/style.css">
|
|
|
|
|
|
</head>
|
|
|
<body class="sidebar-visible no-js">
|
|
|
<div id="body-container">
|
|
|
<!-- Provide site root to javascript -->
|
|
|
<script>
|
|
|
var path_to_root = "../../";
|
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
|
|
</script>
|
|
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
|
<script>
|
|
|
try {
|
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
|
}
|
|
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
|
}
|
|
|
} catch (e) { }
|
|
|
</script>
|
|
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
|
<script>
|
|
|
var theme;
|
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
|
var html = document.querySelector('html');
|
|
|
html.classList.remove('light')
|
|
|
html.classList.add(theme);
|
|
|
var body = document.querySelector('body');
|
|
|
body.classList.remove('no-js')
|
|
|
body.classList.add('js');
|
|
|
</script>
|
|
|
|
|
|
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
|
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
|
<script>
|
|
|
var body = document.querySelector('body');
|
|
|
var sidebar = null;
|
|
|
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
|
|
if (document.body.clientWidth >= 1080) {
|
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
|
sidebar = sidebar || 'visible';
|
|
|
} else {
|
|
|
sidebar = 'hidden';
|
|
|
}
|
|
|
sidebar_toggle.checked = sidebar === 'visible';
|
|
|
body.classList.remove('sidebar-visible');
|
|
|
body.classList.add("sidebar-" + sidebar);
|
|
|
</script>
|
|
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
|
<div class="sidebar-scrollbox">
|
|
|
<ol class="chapter"><li class="chapter-item affix "><a href="../../about-book.html">关于本书</a></li><li class="chapter-item affix "><a href="../../into-rust.html">进入 Rust 编程世界</a></li><li class="chapter-item affix "><a href="../../first-try/sth-you-should-not-do.html">避免从入门到放弃</a></li><li class="chapter-item affix "><a href="../../community.html">社区和锈书</a></li><li class="spacer"></li><li class="chapter-item affix "><a href="../../some-thoughts.html">Datav: 可编程的数据可视化平台和可观测性平台</a></li><li class="chapter-item affix "><li class="part-title">Rust 语言基础学习</li><li class="spacer"></li><li class="chapter-item "><a href="../../first-try/intro.html"><strong aria-hidden="true">1.</strong> 寻找牛刀,以便小试</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../first-try/installation.html"><strong aria-hidden="true">1.1.</strong> 安装 Rust 环境</a></li><li class="chapter-item "><a href="../../first-try/editor.html"><strong aria-hidden="true">1.2.</strong> 墙推 VSCode!</a></li><li class="chapter-item "><a href="../../first-try/cargo.html"><strong aria-hidden="true">1.3.</strong> 认识 Cargo</a></li><li class="chapter-item "><a href="../../first-try/hello-world.html"><strong aria-hidden="true">1.4.</strong> 不仅仅是 Hello world</a></li><li class="chapter-item "><a href="../../first-try/slowly-downloading.html"><strong aria-hidden="true">1.5.</strong> 下载依赖太慢了?</a></li></ol></li><li class="chapter-item expanded "><a href="../../basic/intro.html"><strong aria-hidden="true">2.</strong> Rust 基础入门</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/variable.html"><strong aria-hidden="true">2.1.</strong> 变量绑定与解构</a></li><li class="chapter-item "><a href="../../basic/base-type/index.html"><strong aria-hidden="true">2.2.</strong> 基本类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/base-type/numbers.html"><strong aria-hidden="true">2.2.1.</strong> 数值类型</a></li><li class="chapter-item "><a href="../../basic/base-type/char-bool.html"><strong aria-hidden="true">2.2.2.</strong> 字符、布尔、单元类型</a></li><li class="chapter-item "><a href="../../basic/base-type/statement-expression.html"><strong aria-hidden="true">2.2.3.</strong> 语句与表达式</a></li><li class="chapter-item "><a href="../../basic/base-type/function.html"><strong aria-hidden="true">2.2.4.</strong> 函数</a></li></ol></li><li class="chapter-item "><a href="../../basic/ownership/index.html"><strong aria-hidden="true">2.3.</strong> 所有权和借用</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/ownership/ownership.html"><strong aria-hidden="true">2.3.1.</strong> 所有权</a></li><li class="chapter-item "><a href="../../basic/ownership/borrowing.html"><strong aria-hidden="true">2.3.2.</strong> 引用与借用</a></li></ol></li><li class="chapter-item expanded "><a href="../../basic/compound-type/intro.html"><strong aria-hidden="true">2.4.</strong> 复合类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../../basic/compound-type/string-slice.html" class="active"><strong aria-hidden="true">2.4.1.</strong> 字符串与切片</a></li><li class="chapter-item "><a href="../../basic/compound-type/tuple.html"><strong aria-hidden="true">2.4.2.</strong> 元组</a></li><li class="chapter-item "><a href="../../basic/compound-type/struct.html"><strong aria-hidden="true">2.4.3.</strong> 结构体</a></li><li class="chapter-item "><a href="../../basic/compound-type/enum.html"><strong aria-hidden="true">2.4.4.</strong> 枚举</a></li><li class="chapter-item "><a href="../../basic/compound-type/array.html"><strong aria-hidden="true">2.4.5.</strong> 数组</a></li></ol></li><li class="chapter-item "><a href="../../basic/flow-control.html"><strong aria-hidden="true">2.5.</strong> 流程控制</a></li><li class="chapter-item "><a href="../../basic/match-pattern/intro.html"><strong aria-hidden="true">2.6.</strong> 模式匹配</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/match-pattern/match-if-let.html"><strong aria-hidden="true">2.6.1.</strong> match 和 if let</a></li><li class="chapter-item "><a href="../../basic/match-pattern/option.html"><strong aria-hidden="true">2.6.2.</strong> 解构 Option</a></li><li class="chapter-item "><a href="../../basic/match-pattern/pattern-match.html"><strong aria-hidden="true">2.6.3.</strong> 模式适用场景</a></li><li class="chapter-item "><a href="../../basic/match-pattern/all-patterns.html"><strong aria-hidden="true">2.6.4.</strong> 全模式列表</a></li></ol></li><li class="chapter-item "><a href="../../basic/method.html"><strong aria-hidden="true">2.7.</strong> 方法 Method</a></li><li class="chapter-item "><a href="../../basic/trait/intro.html"><strong aria-hidden="true">2.8.</strong> 泛型和特征</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/trait/generic.html"><strong aria-hidden="true">2.8.1.</strong> 泛型 Generics</a></li><li class="chapter-item "><a href="../../basic/trait/trait.html"><strong aria-hidden="true">2.8.2.</strong> 特征 Trait</a></li><li class="chapter-item "><a href="../../basic/trait/trait-object.html"><strong aria-hidden="true">2.8.3.</strong> 特征对象</a></li><li class="chapter-item "><a href="../../basic/trait/advance-trait.html"><strong aria-hidden="true">2.8.4.</strong> 进一步深入特征</a></li></ol></li><li class="chapter-item "><a href="../../basic/collections/intro.html"><strong aria-hidden="true">2.9.</strong> 集合类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/collections/vector.html"><strong aria-hidden="true">2.9.1.</strong> 动态数组 Vector</a></li><li class="chapter-item "><a href="../../basic/collections/hashmap.html"><strong aria-hidden="true">2.9.2.</strong> KV 存储 HashMap</a></li></ol></li><li class="chapter-item "><a href="../../basic/lifetime.html"><strong aria-hidden="true">2.10.</strong> 认识生命周期</a></li><li class="chapter-item "><a href="../../basic/result-error/intro.html"><strong aria-hidden="true">2.11.</strong> 返回值和错误处理</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/result-error/panic.html"><strong aria-hidden="true">2.11.1.</strong> panic! 深入剖析</a></li><li class="chapter-item "><a href="../../basic/result-error/result.html"><strong aria-hidden="true">2.11.2.</strong> 返回值 Result 和?</a></li></ol></li><li class="chapter-item "><a href="../../basic/crate-module/intro.html"><strong aria-hidden="true">2.12.</strong> 包和模块</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic/crate-module/crate.html"><strong aria-hidden="true">2.12.1.</strong> 包 Crate</a></li><li class="chapter-item "><a href="../../basic/crate-module/module.html"><strong aria-hidden="true">2.12.2.</strong> 模块 Module</a></li><li class="chapter-item "><a href="../../basic/crate-module/use.html"><strong aria-hidden="true">2.12.3.</strong> 使用 use 引入模块及受限可见性</a></li></ol></li><li class="chapter-item "><a href="../../basic/comment.html"><strong aria-hidden="true">2.13.</strong> 注释和文档</a></li><li class="chapter-item "><a href="../../basic/formatted-output.html"><strong aria-hidden="true">2.14.</strong> 格式化输出</a></li></ol></li><li class="chapter-item "><a href="../../basic-practice/intro.html"><strong aria-hidden="true">3.</strong> 入门实战:文件搜索工具</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../basic-practice/base-features.html"><strong aria-hidden="true">3.1.</strong> 基本功能</a></li><li class="chapter-item "><a href="../../basic-practice/refactoring.html"><strong aria-hidden="true">3.2.</strong> 增加模块化和错误处理</a></li><li class="chapter-item "><a href="../../basic-practice/tests.html"><strong aria-hidden="true">3.3.</strong> 测试驱动开发</a></li><li class="chapter-item "><a href="../../basic-practice/envs.html"><strong aria-hidden="true">3.4.</strong> 使用环境变量</a></li><li class="chapter-item "><a href="../../basic-practice/stderr.html"><strong aria-hidden="true">3.5.</strong> 重定向错误信息的输出</a></li><li class="chapter-item "><a href="../../basic-practice/iterators.html"><strong aria-hidden="true">3.6.</strong> 使用迭代器来改进程序(可选)</a></li></ol></li><li class="chapter-item "><li class="part-title">Rust 语言进阶学习</li><li class="spacer"></li><li class="chapter-item "><a href="../../advance/intro.html"><strong aria-hidden="true">4.</strong> Rust 高级进阶</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/lifetime/intro.html"><strong aria-hidden="true">4.1.</strong> 生命周期</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/lifetime/advance.html"><strong aria-hidden="true">4.1.1.</strong> 深入生命周期</a></li><li class="chapter-item "><a href="../../advance/lifetime/static.html"><strong aria-hidden="true">4.1.2.</strong> &'static 和 T: 'static</a></li></ol></li><li class="chapter-item "><a href="../../advance/functional-programing/intro.html"><strong aria-hidden="true">4.2.</strong> 函数式编程: 闭包、迭代器</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/functional-programing/closure.html"><strong aria-hidden="true">4.2.1.</strong> 闭包 Closure</a></li><li class="chapter-item "><a href="../../advance/functional-programing/iterator.html"><strong aria-hidden="true">4.2.2.</strong> 迭代器 Iterator</a></li></ol></li><li class="chapter-item "><a href="../../advance/into-types/intro.html"><strong aria-hidden="true">4.3.</strong> 深入类型</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/into-types/converse.html"><strong aria-hidden="true">4.3.1.</strong> 类型转换</a></li><li class="chapter-item "><a href="../../advance/into-types/custom-type.html"><strong aria-hidden="true">4.3.2.</strong> newtype 和 类型别名</a></li><li class="chapter-item "><a href="../../advance/into-types/sized.html"><strong aria-hidden="true">4.3.3.</strong> Sized 和不定长类型 DST</a></li><li class="chapter-item "><a href="../../advance/into-types/enum-int.html"><strong aria-hidden="true">4.3.4.</strong> 枚举和整数</a></li></ol></li><li class="chapter-item "><a href="../../advance/smart-pointer/intro.html"><strong aria-hidden="true">4.4.</strong> 智能指针</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/smart-pointer/box.html"><strong aria-hidden="true">4.4.1.</strong> Box堆对象分配</a></li><li class="chapter-item "><a href="../../advance/smart-pointer/deref.html"><strong aria-hidden="true">4.4.2.</strong> Deref 解引用</a></li><li class="chapter-item "><a href="../../advance/smart-pointer/drop.html"><strong aria-hidden="true">4.4.3.</strong> Drop 释放资源</a></li><li class="chapter-item "><a href="../../advance/smart-pointer/rc-arc.html"><strong aria-hidden="true">4.4.4.</strong> Rc 与 Arc 实现 1vN 所有权机制</a></li><li class="chapter-item "><a href="../../advance/smart-pointer/cell-refcell.html"><strong aria-hidden="true">4.4.5.</strong> Cell 与 RefCell 内部可变性</a></li></ol></li><li class="chapter-item "><a href="../../advance/circle-self-ref/intro.html"><strong aria-hidden="true">4.5.</strong> 循环引用与自引用</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/circle-self-ref/circle-reference.html"><strong aria-hidden="true">4.5.1.</strong> Weak 与循环引用</a></li><li class="chapter-item "><a href="../../advance/circle-self-ref/self-referential.html"><strong aria-hidden="true">4.5.2.</strong> 结构体中的自引用</a></li></ol></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/intro.html"><strong aria-hidden="true">4.6.</strong> 多线程并发编程</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/concurrency-with-threads/concurrency-parallelism.html"><strong aria-hidden="true">4.6.1.</strong> 并发和并行</a></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/thread.html"><strong aria-hidden="true">4.6.2.</strong> 使用多线程</a></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/message-passing.html"><strong aria-hidden="true">4.6.3.</strong> 线程同步:消息传递</a></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/sync1.html"><strong aria-hidden="true">4.6.4.</strong> 线程同步:锁、Condvar 和信号量</a></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/sync2.html"><strong aria-hidden="true">4.6.5.</strong> 线程同步:Atomic 原子操作与内存顺序</a></li><li class="chapter-item "><a href="../../advance/concurrency-with-threads/send-sync.html"><strong aria-hidden="true">4.6.6.</strong> 基于 Send 和 Sync 的线程安全</a></li></ol></li><li class="chapter-item "><a href="../../advance/global-variable.html"><strong aria-hidden="true">4.7.</strong> 全局变量</a></li><li class="chapter-item "><a href="../../advance/errors.html"><strong aria-hidden="true">4.8.</strong> 错误处理</a></li><li class="chapter-item "><a href="../../advance/unsafe/intro.html"><strong aria-hidden="true">4.9.</strong> Unsafe Rust</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/unsafe/superpowers.html"><strong aria-hidden="true">4.9.1.</strong> 五种兵器</a></li><li class="chapter-item "><a href="../../advance/unsafe/inline-asm.html"><strong aria-hidden="true">4.9.2.</strong> 内联汇编</a></li></ol></li><li class="chapter-item "><a href="../../advance/macro.html"><strong aria-hidden="true">4.10.</strong> Macro 宏编程</a></li><li class="chapter-item "><a href="../../advance/async/intro.html"><strong aria-hidden="true">4.11.</strong> async/await 异步编程</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance/async/getting-started.html"><strong aria-hidden="true">4.11.1.</strong> async 编程入门</a></li><li class="chapter-item "><a href="../../advance/async/future-excuting.html"><strong aria-hidden="true">4.11.2.</strong> 底层探秘: Future 执行与任务调度</a></li><li class="chapter-item "><a href="../../advance/async/pin-unpin.html"><strong aria-hidden="true">4.11.3.</strong> 定海神针 Pin 和 Unpin</a></li><li class="chapter-item "><a href="../../advance/async/async-await.html"><strong aria-hidden="true">4.11.4.</strong> async/await 和 Stream 流处理</a></li><li class="chapter-item "><a href="../../advance/async/multi-futures-simultaneous.html"><strong aria-hidden="true">4.11.5.</strong> 同时运行多个 Future</a></li><li class="chapter-item "><a href="../../advance/async/pain-points-and-workarounds.html"><strong aria-hidden="true">4.11.6.</strong> 一些疑难问题的解决办法</a></li><li class="chapter-item "><a href="../../advance/async/web-server.html"><strong aria-hidden="true">4.11.7.</strong> 实践应用:Async Web 服务器</a></li></ol></li></ol></li><li class="chapter-item "><a href="../../advance-practice1/intro.html"><strong aria-hidden="true">5.</strong> 进阶实战1: 实现一个 web 服务器</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance-practice1/web-server.html"><strong aria-hidden="true">5.1.</strong> 单线程版本</a></li><li class="chapter-item "><a href="../../advance-practice1/multi-threads.html"><strong aria-hidden="true">5.2.</strong> 多线程版本</a></li><li class="chapter-item "><a href="../../advance-practice1/graceful-shutdown.html"><strong aria-hidden="true">5.3.</strong> 优雅关闭和资源清理</a></li></ol></li><li class="chapter-item "><a href="../../advance-practice/intro.html"><strong aria-hidden="true">6.</strong> 进阶实战2: 实现一个简单 Redis</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../advance-practice/overview.html"><strong aria-hidden="true">6.1.</strong> tokio 概览</a></li><li class="chapter-item "><a href="../../advance-practice/getting-startted.html"><strong aria-hidden="true">6.2.</strong> 使用初印象</a></li><li class="chapter-item "><a href="../../advance-practice/spawning.html"><strong aria-hidden="true">6.3.</strong> 创建异步任务</a></li><li class="chapter-item "><a href="../../advance-practice/shared-state.html"><strong aria-hidden="true">6.4.</strong> 共享状态</a></li><li class="chapter-item "><a href="../../advance-practice/channels.html"><strong aria-hidden="true">6.5.</strong> 消息传递</a></li><li class="chapter-item "><a href="../../advance-practice/io.html"><strong aria-hidden="true">6.6.</strong> I/O</a></li><li class="chapter-item "><a href="../../advance-practice/frame.html"><strong aria-hidden="true">6.7.</strong> 解析数据帧</a></li><li class="chapter-item "><a href="../../advance-practice/async.html"><strong aria-hidden="true">6.8.</strong> 深入 async</a></li><li class="chapter-item "><a href="../../advance-practice/select.html"><strong aria-hidden="true">6.9.</strong> select</a></li><li class="chapter-item "><a href="../../advance-practice/stream.html"><strong aria-hidden="true">6.10.</strong> 类似迭代器的 Stream</a></li><li class="chapter-item "><a href="../../advance-practice/graceful-shutdown.html"><strong aria-hidden="true">6.11.</strong> 优雅的关闭</a></li><li class="chapter-item "><a href="../../advance-practice/bridging-with-sync.html"><strong aria-hidden="true">6.12.</strong> 异步跟同步共存</a></li></ol></li><li class="chapter-item "><a href="../../difficulties/intro.html"><strong aria-hidden="true">7.</strong> Rust 难点攻关</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../difficulties/slice.html"><strong aria-hidden="true">7.1.</strong> 切片和切片引用</a></li><li class="chapter-item "><a href="../../difficulties/eq.html"><strong aria-hidden="true">7.2.</strong> Eq 和 PartialEq</a></li><li class="chapter-item "><a href="../../difficulties/string.html"><strong aria-hidden="true">7.3.</strong> String、&str 和 str TODO</a></li><li class="chapter-item "><a href="../../difficulties/lifetime.html"><strong aria-hidden="true">7.4.</strong> 作用域、生命周期和 NLL TODO</a></li><li class="chapter-item "><a href="../../difficulties/move-copy.html"><strong aria-hidden="true">7.5.</strong> move、Copy 和 Clone TODO</a></li><li class="chapter-item "><a href="../../advance/difficulties/pointer.html"><strong aria-hidden="true">7.6.</strong> 裸指针、引用和智能指针 TODO</a></li></ol></li><li class="chapter-item "><li class="part-title">常用工具链</li><li class="spacer"></li><li class="chapter-item "><a href="../../test/intro.html"><strong aria-hidden="true">8.</strong> 自动化测试</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../test/write-tests.html"><strong aria-hidden="true">8.1.</strong> 编写测试及控制执行</a></li><li class="chapter-item "><a href="../../test/unit-integration-test.html"><strong aria-hidden="true">8.2.</strong> 单元测试和集成测试</a></li><li class="chapter-item "><a href="../../test/assertion.html"><strong aria-hidden="true">8.3.</strong> 断言 assertion</a></li><li class="chapter-item "><a href="../../test/ci.html"><strong aria-hidden="true">8.4.</strong> 用 GitHub Actions 进行持续集成</a></li><li class="chapter-item "><a href="../../test/benchmark.html"><strong aria-hidden="true">8.5.</strong> 基准测试 benchmark</a></li></ol></li><li class="chapter-item "><a href="../../cargo/intro.html"><strong aria-hidden="true">9.</strong> Cargo 使用指南</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../cargo/getting-started.html"><strong aria-hidden="true">9.1.</strong> 上手使用</a></li><li class="chapter-item "><a href="../../cargo/guide/intro.html"><strong aria-hidden="true">9.2.</strong> 基础指南</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../cargo/guide/why-exist.html"><strong aria-hidden="true">9.2.1.</strong> 为何会有 Cargo</a></li><li class="chapter-item "><a href="../../cargo/guide/download-package.html"><strong aria-hidden="true">9.2.2.</strong> 下载并构建 Package</a></li><li class="chapter-item "><a href="../../cargo/guide/dependencies.html"><strong aria-hidden="true">9.2.3.</strong> 添加依赖</a></li><li class="chapter-item "><a href="../../cargo/guide/package-layout.html"><strong aria-hidden="true">9.2.4.</strong> Package 目录结构</a></li><li class="chapter-item "><a href="../../cargo/guide/cargo-toml-lock.html"><strong aria-hidden="true">9.2.5.</strong> Cargo.toml vs Cargo.lock</a></li><li class="chapter-item "><a href="../../cargo/guide/tests-ci.html"><strong aria-hidden="true">9.2.6.</strong> 测试和 CI</a></li><li class="chapter-item "><a href="../../cargo/guide/cargo-cache.html"><strong aria-hidden="true">9.2.7.</strong> Cargo 缓存</a></li><li class="chapter-item "><a href="../../cargo/guide/build-cache.html"><strong aria-hidden="true">9.2.8.</strong> Build 缓存</a></li></ol></li><li class="chapter-item "><a href="../../cargo/reference/intro.html"><strong aria-hidden="true">9.3.</strong> 进阶指南</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../cargo/reference/specify-deps.html"><strong aria-hidden="true">9.3.1.</strong> 指定依赖项</a></li><li class="chapter-item "><a href="../../cargo/reference/deps-overriding.html"><strong aria-hidden="true">9.3.2.</strong> 依赖覆盖</a></li><li class="chapter-item "><a href="../../cargo/reference/manifest.html"><strong aria-hidden="true">9.3.3.</strong> Cargo.toml 清单详解</a></li><li class="chapter-item "><a href="../../cargo/reference/cargo-target.html"><strong aria-hidden="true">9.3.4.</strong> Cargo Target</a></li><li class="chapter-item "><a href="../../cargo/reference/workspaces.html"><strong aria-hidden="true">9.3.5.</strong> 工作空间 Workspace</a></li><li class="chapter-item "><a href="../../cargo/reference/features/intro.html"><strong aria-hidden="true">9.3.6.</strong> 条件编译 Features</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../cargo/reference/features/examples.html"><strong aria-hidden="true">9.3.6.1.</strong> Features 示例</a></li></ol></li><li class="chapter-item "><a href="../../cargo/reference/profiles.html"><strong aria-hidden="true">9.3.7.</strong> 发布配置 Profile</a></li><li class="chapter-item "><a href="../../cargo/reference/configuration.html"><strong aria-hidden="true">9.3.8.</strong> 通过 config.toml 对 Cargo 进行配置</a></li><li class="chapter-item "><a href="../../cargo/reference/publishing-on-crates.io.html"><strong aria-hidden="true">9.3.9.</strong> 发布到 crates.io</a></li><li class="chapter-item "><a href="../../cargo/reference/build-script/intro.html"><strong aria-hidden="true">9.3.10.</strong> 构建脚本 build.rs</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../cargo/reference/build-script/examples.html"><strong aria-hidden="true">9.3.10.1.</strong> 构建脚本示例</a></li></ol></li></ol></li></ol></li><li class="chapter-item "><li class="part-title">开发实践</li><li class="spacer"></li><li class="chapter-item "><a href="../../usecases/intro.html"><strong aria-hidden="true">10.</strong> 企业落地实践</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../usecases/aws-rust.html"><strong aria-hidden="true">10.1.</strong> AWS 为何这么喜欢 Rust?</a></li></ol></li><li class="chapter-item "><a href="../../logs/intro.html"><strong aria-hidden="true">11.</strong> 日志和监控</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../logs/about-log.html"><strong aria-hidden="true">11.1.</strong> 日志详解</a></li><li class="chapter-item "><a href="../../logs/log.html"><strong aria-hidden="true">11.2.</strong> 日志门面 log</a></li><li class="chapter-item "><a href="../../logs/tracing.html"><strong aria-hidden="true">11.3.</strong> 使用 tracing 记录日志</a></li><li class="chapter-item "><a href="../../logs/tracing-logger.html"><strong aria-hidden="true">11.4.</strong> 自定义 tracing 的输出格式</a></li><li class="chapter-item "><a href="../../logs/observe/intro.html"><strong aria-hidden="true">11.5.</strong> 监控</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../logs/observe/about-observe.html"><strong aria-hidden="true">11.5.1.</strong> 可观测性</a></li><li class="chapter-item "><a href="../../logs/observe/trace.html"><strong aria-hidden="true">11.5.2.</strong> 分布式追踪</a></li></ol></li></ol></li><li class="chapter-item "><a href="../../practice/intro.html"><strong aria-hidden="true">12.</strong> Rust 最佳实践</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../practice/third-party-libs.html"><strong aria-hidden="true">12.1.</strong> 日常开发三方库精选</a></li><li class="chapter-item "><a href="../../practice/naming.html"><strong aria-hidden="true">12.2.</strong> 命名规范</a></li><li class="chapter-item "><a href="../../practice/interview.html"><strong aria-hidden="true">12.3.</strong> 面试经验</a></li><li class="chapter-item "><a href="../../practice/best-pratice.html"><strong aria-hidden="true">12.4.</strong> 代码开发实践 todo</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/intro.html"><strong aria-hidden="true">13.</strong> 手把手带你实现链表</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/do-we-need-it.html"><strong aria-hidden="true">13.1.</strong> 我们到底需不需要链表</a></li><li class="chapter-item "><a href="../../too-many-lists/bad-stack/intro.html"><strong aria-hidden="true">13.2.</strong> 不太优秀的单向链表:栈</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/bad-stack/layout.html"><strong aria-hidden="true">13.2.1.</strong> 数据布局</a></li><li class="chapter-item "><a href="../../too-many-lists/bad-stack/basic-operations.html"><strong aria-hidden="true">13.2.2.</strong> 基本操作</a></li><li class="chapter-item "><a href="../../too-many-lists/bad-stack/final-code.html"><strong aria-hidden="true">13.2.3.</strong> 最后实现</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/ok-stack/intro.html"><strong aria-hidden="true">13.3.</strong> 还可以的单向链表</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/ok-stack/type-optimizing.html"><strong aria-hidden="true">13.3.1.</strong> 优化类型定义</a></li><li class="chapter-item "><a href="../../too-many-lists/ok-stack/peek.html"><strong aria-hidden="true">13.3.2.</strong> 定义 Peek 函数</a></li><li class="chapter-item "><a href="../../too-many-lists/ok-stack/iter.html"><strong aria-hidden="true">13.3.3.</strong> IntoIter 和 Iter</a></li><li class="chapter-item "><a href="../../too-many-lists/ok-stack/itermut.html"><strong aria-hidden="true">13.3.4.</strong> IterMut 以及完整代码</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/persistent-stack/intro.html"><strong aria-hidden="true">13.4.</strong> 持久化单向链表</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/persistent-stack/layout.html"><strong aria-hidden="true">13.4.1.</strong> 数据布局和基本操作</a></li><li class="chapter-item "><a href="../../too-many-lists/persistent-stack/drop-arc.html"><strong aria-hidden="true">13.4.2.</strong> Drop、Arc 及完整代码</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/deque/intro.html"><strong aria-hidden="true">13.5.</strong> 不咋样的双端队列</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/deque/layout.html"><strong aria-hidden="true">13.5.1.</strong> 数据布局和基本操作</a></li><li class="chapter-item "><a href="../../too-many-lists/deque/peek.html"><strong aria-hidden="true">13.5.2.</strong> Peek</a></li><li class="chapter-item "><a href="../../too-many-lists/deque/symmetric.html"><strong aria-hidden="true">13.5.3.</strong> 基本操作的对称镜像</a></li><li class="chapter-item "><a href="../../too-many-lists/deque/iterator.html"><strong aria-hidden="true">13.5.4.</strong> 迭代器</a></li><li class="chapter-item "><a href="../../too-many-lists/deque/final-code.html"><strong aria-hidden="true">13.5.5.</strong> 最终代码</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/intro.html"><strong aria-hidden="true">13.6.</strong> 不错的 unsafe 队列</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/layout.html"><strong aria-hidden="true">13.6.1.</strong> 数据布局</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/basics.html"><strong aria-hidden="true">13.6.2.</strong> 基本操作</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/miri.html"><strong aria-hidden="true">13.6.3.</strong> Miri</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/stacked-borrow.html"><strong aria-hidden="true">13.6.4.</strong> 栈借用</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/testing-stacked-borrow.html"><strong aria-hidden="true">13.6.5.</strong> 测试栈借用</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/layout2.html"><strong aria-hidden="true">13.6.6.</strong> 数据布局 2</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/extra-junk.html"><strong aria-hidden="true">13.6.7.</strong> 额外的操作</a></li><li class="chapter-item "><a href="../../too-many-lists/unsafe-queue/final-code.html"><strong aria-hidden="true">13.6.8.</strong> 最终代码</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/intro.html"><strong aria-hidden="true">13.7.</strong> 生产级的双向 unsafe 队列</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/layout.html"><strong aria-hidden="true">13.7.1.</strong> 数据布局</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/variance-and-phantomData.html"><strong aria-hidden="true">13.7.2.</strong> 型变与子类型</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/basics.html"><strong aria-hidden="true">13.7.3.</strong> 基础结构</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/drop-and-panic-safety.html"><strong aria-hidden="true">13.7.4.</strong> 恐慌与安全</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/boring-combinatorics.html"><strong aria-hidden="true">13.7.5.</strong> 无聊的组合</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/filling-in-random-bits.html"><strong aria-hidden="true">13.7.6.</strong> 其它特征</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/testing.html"><strong aria-hidden="true">13.7.7.</strong> 测试</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/send-sync-and-compile-tests.html"><strong aria-hidden="true">13.7.8.</strong> Send,Sync和编译测试</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/implementing-cursors.html"><strong aria-hidden="true">13.7.9.</strong> 实现游标</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/testing-cursors.html"><strong aria-hidden="true">13.7.10.</strong> 测试游标</a></li><li class="chapter-item "><a href="../../too-many-lists/production-unsafe-deque/final-code.html"><strong aria-hidden="true">13.7.11.</strong> 最终代码</a></li></ol></li><li class="chapter-item "><a href="../../too-many-lists/advanced-lists/intro.html"><strong aria-hidden="true">13.8.</strong> 使用高级技巧实现链表</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../too-many-lists/advanced-lists/double-singly.html"><strong aria-hidden="true">13.8.1.</strong> 双单向链表</a></li><li class="chapter-item "><a href="../../too-many-lists/advanced-lists/stack-allocated.html"><strong aria-hidden="true">13.8.2.</strong> 栈上的链表</a></li></ol></li></ol></li><li class="chapter-item "><li class="part-title">攻克编译错误</li><li class="spacer"></li><li class="chapter-item "><a href="../../compiler/intro.html"><strong aria-hidden="true">14.</strong> 征服编译错误</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../compiler/fight-with-compiler/intro.html"><strong aria-hidden="true">14.1.</strong> 对抗编译检查</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../compiler/fight-with-compiler/lifetime/intro.html"><strong aria-hidden="true">14.1.1.</strong> 生命周期</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../compiler/fight-with-compiler/lifetime/too-long1.html"><strong aria-hidden="true">14.1.1.1.</strong> 生命周期过大-01</a></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/lifetime/too-long2.html"><strong aria-hidden="true">14.1.1.2.</strong> 生命周期过大-02</a></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/lifetime/loop.html"><strong aria-hidden="true">14.1.1.3.</strong> 循环中的生命周期</a></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/lifetime/closure-with-static.html"><strong aria-hidden="true">14.1.1.4.</strong> 闭包碰到特征对象-01</a></li></ol></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/borrowing/intro.html"><strong aria-hidden="true">14.1.2.</strong> 重复借用</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../compiler/fight-with-compiler/borrowing/ref-exist-in-out-fn.html"><strong aria-hidden="true">14.1.2.1.</strong> 同时在函数内外使用引用</a></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/borrowing/borrow-distinct-fields-of-struct.html"><strong aria-hidden="true">14.1.2.2.</strong> 智能指针引起的重复借用错误</a></li></ol></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/unconstrained.html"><strong aria-hidden="true">14.1.3.</strong> 类型未限制(todo)</a></li><li class="chapter-item "><a href="../../compiler/fight-with-compiler/phantom-data.html"><strong aria-hidden="true">14.1.4.</strong> 幽灵数据(todo)</a></li></ol></li><li class="chapter-item "><a href="../../compiler/pitfalls/index.html"><strong aria-hidden="true">14.2.</strong> Rust 常见陷阱</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../compiler/pitfalls/use-vec-in-for.html"><strong aria-hidden="true">14.2.1.</strong> for 循环中使用外部数组</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/stack-overflow.html"><strong aria-hidden="true">14.2.2.</strong> 线程类型导致的栈溢出</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/arithmetic-overflow.html"><strong aria-hidden="true">14.2.3.</strong> 算术溢出导致的 panic</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/closure-with-lifetime.html"><strong aria-hidden="true">14.2.4.</strong> 闭包中奇怪的生命周期</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/the-disabled-mutability.html"><strong aria-hidden="true">14.2.5.</strong> 可变变量不可变?</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/multiple-mutable-references.html"><strong aria-hidden="true">14.2.6.</strong> 可变借用失败引发的深入思考</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/lazy-iterators.html"><strong aria-hidden="true">14.2.7.</strong> 不太勤快的迭代器</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/weird-ranges.html"><strong aria-hidden="true">14.2.8.</strong> 奇怪的序列 x..y</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/iterator-everywhere.html"><strong aria-hidden="true">14.2.9.</strong> 无处不在的迭代器</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/main-with-channel-blocked.html"><strong aria-hidden="true">14.2.10.</strong> 线程间传递消息导致主线程无法结束</a></li><li class="chapter-item "><a href="../../compiler/pitfalls/utf8-performance.html"><strong aria-hidden="true">14.2.11.</strong> 警惕 UTF-8 引发的性能隐患</a></li></ol></li></ol></li><li class="chapter-item "><li class="part-title">性能优化</li><li class="spacer"></li><li class="chapter-item "><a href="../../profiling/intro.html"><strong aria-hidden="true">15.</strong> Rust 性能优化 todo</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../profiling/memory/intro.html"><strong aria-hidden="true">15.1.</strong> 深入内存 todo</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../profiling/memory/pointer-ref.html"><strong aria-hidden="true">15.1.1.</strong> 指针和引用 todo</a></li><li class="chapter-item "><a href="../../profiling/memory/uninit.html"><strong aria-hidden="true">15.1.2.</strong> 未初始化内存 todo</a></li><li class="chapter-item "><a href="../../profiling/memory/allocation.html"><strong aria-hidden="true">15.1.3.</strong> 内存分配 todo</a></li><li class="chapter-item "><a href="../../profiling/memory/layout.html"><strong aria-hidden="true">15.1.4.</strong> 内存布局 todo</a></li><li class="chapter-item "><a href="../../profiling/memory/virtual.html"><strong aria-hidden="true">15.1.5.</strong> 虚拟内存 todo</a></li></ol></li><li class="chapter-item "><a href="../../profiling/performance/intro.html"><strong aria-hidden="true">15.2.</strong> 性能调优 doing</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../profiling/performance/string.html"><strong aria-hidden="true">15.2.1.</strong> 字符串操作性能</a></li><li class="chapter-item "><a href="../../profiling/performance/deep-into-move.html"><strong aria-hidden="true">15.2.2.</strong> 深入理解 move</a></li><li class="chapter-item "><a href="../../profiling/performance/early-optimise.html"><strong aria-hidden="true">15.2.3.</strong> 糟糕的提前优化 todo</a></li><li class="chapter-item "><a href="../../profiling/performance/clone-copy.html"><strong aria-hidden="true">15.2.4.</strong> Clone 和 Copy todo</a></li><li class="chapter-item "><a href="../../profiling/performance/runtime-check.html"><strong aria-hidden="true">15.2.5.</strong> 减少 Runtime check(todo)</a></li><li class="chapter-item "><a href="../../profiling/performance/cpu-cache.html"><strong aria-hidden="true">15.2.6.</strong> CPU 缓存性能优化 todo</a></li><li class="chapter-item "><a href="../../profiling/performance/calculate.html"><strong aria-hidden="true">15.2.7.</strong> 计算性能优化 todo</a></li><li class="chapter-item "><a href="../../profiling/performance/heap-stack.html"><strong aria-hidden="true">15.2.8.</strong> 堆和栈 todo</a></li><li class="chapter-item "><a href="../../profiling/performance/allocator.html"><strong aria-hidden="true">15.2.9.</strong> 内存 allocator todo</a></li><li class="chapter-item "><a href="../../profiling/performance/tools.html"><strong aria-hidden="true">15.2.10.</strong> 常用性能测试工具 todo</a></li><li class="chapter-item "><a href="../../profiling/performance/enum.html"><strong aria-hidden="true">15.2.11.</strong> Enum 内存优化 todo</a></li></ol></li><li class="chapter-item "><a href="../../profiling/compiler/intro.html"><strong aria-hidden="true">15.3.</strong> 编译优化 todo</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../profiling/compiler/llvm.html"><strong aria-hidden="true">15.3.1.</strong> LLVM todo</a></li><li class="chapter-item "><a href="../../profiling/compiler/attributes.html"><strong aria-hidden="true">15.3.2.</strong> 常见属性标记 todo</a></li><li class="chapter-item "><a href="../../profiling/compiler/speed-up.html"><strong aria-hidden="true">15.3.3.</strong> 提升编译速度 todo</a></li><li class="chapter-item "><a href="../../profiling/compiler/optimization/intro.html"><strong aria-hidden="true">15.3.4.</strong> 编译器优化 todo</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../profiling/compiler/optimization/option.html"><strong aria-hidden="true">15.3.4.1.</strong> Option 枚举 todo</a></li></ol></li></ol></li></ol></li><li class="chapter-item "><li class="part-title">附录</li><li class="spacer"></li><li class="chapter-item "><div><strong aria-hidden="true">16.</strong> Appendix</div><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../appendix/keywords.html"><strong aria-hidden="true">16.1.</strong> 关键字</a></li><li class="chapter-item "><a href="../../appendix/operators.html"><strong aria-hidden="true">16.2.</strong> 运算符与符号</a></li><li class="chapter-item "><a href="../../appendix/expressions.html"><strong aria-hidden="true">16.3.</strong> 表达式</a></li><li class="chapter-item "><a href="../../appendix/derive.html"><strong aria-hidden="true">16.4.</strong> 派生特征 trait</a></li><li class="chapter-item "><a href="../../appendix/prelude.html"><strong aria-hidden="true">16.5.</strong> prelude 模块 todo</a></li><li class="chapter-item "><a href="../../appendix/rust-version.html"><strong aria-hidden="true">16.6.</strong> Rust 版本说明</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/intro.html"><strong aria-hidden="true">16.7.</strong> Rust 历次版本更新解读</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../appendix/rust-versions/1.58.html"><strong aria-hidden="true">16.7.1.</strong> 1.58</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.59.html"><strong aria-hidden="true">16.7.2.</strong> 1.59</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.60.html"><strong aria-hidden="true">16.7.3.</strong> 1.60</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.61.html"><strong aria-hidden="true">16.7.4.</strong> 1.61</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.62.html"><strong aria-hidden="true">16.7.5.</strong> 1.62</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.63.html"><strong aria-hidden="true">16.7.6.</strong> 1.63</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.64.html"><strong aria-hidden="true">16.7.7.</strong> 1.64</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.65.html"><strong aria-hidden="true">16.7.8.</strong> 1.65</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.66.html"><strong aria-hidden="true">16.7.9.</strong> 1.66</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.67.html"><strong aria-hidden="true">16.7.10.</strong> 1.67</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.68.html"><strong aria-hidden="true">16.7.11.</strong> 1.68</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.69.html"><strong aria-hidden="true">16.7.12.</strong> 1.69</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.70.html"><strong aria-hidden="true">16.7.13.</strong> 1.70</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.71.html"><strong aria-hidden="true">16.7.14.</strong> 1.71</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.72.html"><strong aria-hidden="true">16.7.15.</strong> 1.72</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.73.html"><strong aria-hidden="true">16.7.16.</strong> 1.73</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.74.html"><strong aria-hidden="true">16.7.17.</strong> 1.74</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.75.html"><strong aria-hidden="true">16.7.18.</strong> 1.75</a></li><li class="chapter-item "><a href="../../appendix/rust-versions/1.76.html"><strong aria-hidden="true">16.7.19.</strong> 1.76</a></li></ol></li></ol></li></ol>
|
|
|
</div>
|
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
|
|
<div class="sidebar-resize-indicator"></div>
|
|
|
</div>
|
|
|
</nav>
|
|
|
|
|
|
<!-- Track and set sidebar scroll position -->
|
|
|
<script>
|
|
|
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
|
|
sidebarScrollbox.addEventListener('click', function(e) {
|
|
|
if (e.target.tagName === 'A') {
|
|
|
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
|
|
}
|
|
|
}, { passive: true });
|
|
|
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
|
|
sessionStorage.removeItem('sidebar-scroll');
|
|
|
if (sidebarScrollTop) {
|
|
|
// preserve sidebar scroll position when navigating via links within sidebar
|
|
|
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
|
|
} else {
|
|
|
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
|
|
var activeSection = document.querySelector('#sidebar .active');
|
|
|
if (activeSection) {
|
|
|
activeSection.scrollIntoView({ block: 'center' });
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
|
|
<div class="page">
|
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
|
<div id="menu-bar" class="menu-bar sticky">
|
|
|
<div class="left-buttons">
|
|
|
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
|
<i class="fa fa-bars"></i>
|
|
|
</label>
|
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
|
<i class="fa fa-paint-brush"></i>
|
|
|
</button>
|
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
|
</ul>
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
|
<i class="fa fa-search"></i>
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<h1 class="menu-title">Rust语言圣经(Rust Course)</h1>
|
|
|
|
|
|
<div class="right-buttons">
|
|
|
<a href="../../print.html" title="Print this book" aria-label="Print this book">
|
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
|
</a>
|
|
|
<a href="https://github.com/sunface/rust-course" title="Git repository" aria-label="Git repository">
|
|
|
<i id="git-repository-button" class="fa fa-github"></i>
|
|
|
</a>
|
|
|
<a href="https://github.com/sunface/rust-course/edit/main/src/basic/compound-type/string-slice.md" title="Suggest an edit" aria-label="Suggest an edit">
|
|
|
<i id="git-edit-button" class="fa fa-edit"></i>
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
|
</form>
|
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
|
<ul id="searchresults">
|
|
|
</ul>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
|
<script>
|
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<div id="content" class="content">
|
|
|
<!-- Page table of contents -->
|
|
|
<div class="sidetoc"><nav class="pagetoc"></nav></div>
|
|
|
<main>
|
|
|
<h1 id="字符串"><a class="header" href="#字符串">字符串</a></h1>
|
|
|
<p>在其他语言中,字符串往往是送分题,因为实在是太简单了,例如 <code>"hello, world"</code> 就是字符串章节的几乎全部内容了,但是如果你带着同样的想法来学 Rust,我保证,绝对会栽跟头,<strong>因此这一章大家一定要重视,仔细阅读,这里有很多其它 Rust 书籍中没有的内容</strong>。</p>
|
|
|
<p>首先来看段很简单的代码:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let my_name = "Pascal";
|
|
|
greet(my_name);
|
|
|
}
|
|
|
|
|
|
fn greet(name: String) {
|
|
|
println!("Hello, {}!", name);
|
|
|
}</code></pre></pre>
|
|
|
<p><code>greet</code> 函数接受一个字符串类型的 <code>name</code> 参数,然后打印到终端控制台中,非常好理解,你们猜猜,这段代码能否通过编译?</p>
|
|
|
<pre><code class="language-conole">error[E0308]: mismatched types
|
|
|
--> src/main.rs:3:11
|
|
|
|
|
|
|
3 | greet(my_name);
|
|
|
| ^^^^^^^
|
|
|
| |
|
|
|
| expected struct `std::string::String`, found `&str`
|
|
|
| help: try using a conversion method: `my_name.to_string()`
|
|
|
|
|
|
error: aborting due to previous error
|
|
|
</code></pre>
|
|
|
<p>Bingo,果然报错了,编译器提示 <code>greet</code> 函数需要一个 <code>String</code> 类型的字符串,却传入了一个 <code>&str</code> 类型的字符串,相信读者心中现在一定有几头草泥马呼啸而过,怎么字符串也能整出这么多花活?</p>
|
|
|
<p>在讲解字符串之前,先来看看什么是切片?</p>
|
|
|
<h2 id="切片slice"><a class="header" href="#切片slice">切片(slice)</a></h2>
|
|
|
<p>切片并不是 Rust 独有的概念,在 Go 语言中就非常流行,它允许你引用集合中部分连续的元素序列,而不是引用整个集合。</p>
|
|
|
<p>对于字符串而言,切片就是对 <code>String</code> 类型中某一部分的引用,它看起来像这样:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s = String::from("hello world");
|
|
|
|
|
|
let hello = &s[0..5];
|
|
|
let world = &s[6..11];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p><code>hello</code> 没有引用整个 <code>String s</code>,而是引用了 <code>s</code> 的一部分内容,通过 <code>[0..5]</code> 的方式来指定。</p>
|
|
|
<p>这就是创建切片的语法,使用方括号包括的一个序列:<strong>[开始索引..终止索引]</strong>,其中开始索引是切片中第一个元素的索引位置,而终止索引是最后一个元素后面的索引位置,也就是这是一个 <code>右半开区间</code>。在切片数据结构内部会保存开始的位置和切片的长度,其中长度是通过 <code>终止索引</code> - <code>开始索引</code> 的方式计算得来的。</p>
|
|
|
<p>对于 <code>let world = &s[6..11];</code> 来说,<code>world</code> 是一个切片,该切片的指针指向 <code>s</code> 的第 7 个字节(索引从 0 开始, 6 是第 7 个字节),且该切片的长度是 <code>5</code> 个字节。</p>
|
|
|
<img alt="" src="https://pic1.zhimg.com/80/v2-69da917741b2c610732d8526a9cc86f5_1440w.jpg" class="center" style="width: 50%;" />
|
|
|
<p>在使用 Rust 的 <code>..</code> <a href="https://course.rs/basic/base-type/numbers.html#%E5%BA%8F%E5%88%97range">range 序列</a>语法时,如果你想从索引 0 开始,可以使用如下的方式,这两个是等效的:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s = String::from("hello");
|
|
|
|
|
|
let slice = &s[0..2];
|
|
|
let slice = &s[..2];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>同样的,如果你的切片想要包含 <code>String</code> 的最后一个字节,则可以这样使用:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s = String::from("hello");
|
|
|
|
|
|
let len = s.len();
|
|
|
|
|
|
let slice = &s[4..len];
|
|
|
let slice = &s[4..];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>你也可以截取完整的 <code>String</code> 切片:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s = String::from("hello");
|
|
|
|
|
|
let len = s.len();
|
|
|
|
|
|
let slice = &s[0..len];
|
|
|
let slice = &s[..];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<blockquote>
|
|
|
<p>在对字符串使用切片语法时需要格外小心,切片的索引必须落在字符之间的边界位置,也就是 UTF-8 字符的边界,例如中文在 UTF-8 中占用三个字节,下面的代码就会崩溃:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span> let s = "中国人";
|
|
|
let a = &s[0..2];
|
|
|
println!("{}",a);
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>因为我们只取 <code>s</code> 字符串的前两个字节,但是本例中每个汉字占用三个字节,因此没有落在边界处,也就是连 <code>中</code> 字都取不完整,此时程序会直接崩溃退出,如果改成 <code>&s[0..3]</code>,则可以正常通过编译。
|
|
|
因此,当你需要对字符串做切片索引操作时,需要格外小心这一点, 关于该如何操作 UTF-8 字符串,参见<a href="#%E6%93%8D%E4%BD%9C-utf-8-%E5%AD%97%E7%AC%A6%E4%B8%B2">这里</a>。</p>
|
|
|
</blockquote>
|
|
|
<p>字符串切片的类型标识是 <code>&str</code>,因此我们可以这样声明一个函数,输入 <code>String</code> 类型,返回它的切片: <code>fn first_word(s: &String) -> &str </code>。</p>
|
|
|
<p>有了切片就可以写出这样的代码:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut s = String::from("hello world");
|
|
|
|
|
|
let word = first_word(&s);
|
|
|
|
|
|
s.clear(); // error!
|
|
|
|
|
|
println!("the first word is: {}", word);
|
|
|
}
|
|
|
fn first_word(s: &String) -> &str {
|
|
|
&s[..1]
|
|
|
}</code></pre></pre>
|
|
|
<p>编译器报错如下:</p>
|
|
|
<pre><code class="language-console">error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
|
|
|
--> src/main.rs:18:5
|
|
|
|
|
|
|
16 | let word = first_word(&s);
|
|
|
| -- immutable borrow occurs here
|
|
|
17 |
|
|
|
18 | s.clear(); // error!
|
|
|
| ^^^^^^^^^ mutable borrow occurs here
|
|
|
19 |
|
|
|
20 | println!("the first word is: {}", word);
|
|
|
| ---- immutable borrow later used here
|
|
|
</code></pre>
|
|
|
<p>回忆一下借用的规则:当我们已经有了可变借用时,就无法再拥有不可变的借用。因为 <code>clear</code> 需要清空改变 <code>String</code>,因此它需要一个可变借用(利用 VSCode 可以看到该方法的声明是 <code>pub fn clear(&mut self)</code> ,参数是对自身的可变借用 );而之后的 <code>println!</code> 又使用了不可变借用,也就是在 <code>s.clear()</code> 处可变借用与不可变借用试图同时生效,因此编译无法通过。</p>
|
|
|
<p>从上述代码可以看出,Rust 不仅让我们的 <code>API</code> 更加容易使用,而且也在编译期就消除了大量错误!</p>
|
|
|
<h4 id="其它切片"><a class="header" href="#其它切片">其它切片</a></h4>
|
|
|
<p>因为切片是对集合的部分引用,因此不仅仅字符串有切片,其它集合类型也有,例如数组:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let a = [1, 2, 3, 4, 5];
|
|
|
|
|
|
let slice = &a[1..3];
|
|
|
|
|
|
assert_eq!(slice, &[2, 3]);
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>该数组切片的类型是 <code>&[i32]</code>,数组切片和字符串切片的工作方式是一样的,例如持有一个引用指向原始数组的某个元素和长度。</p>
|
|
|
<h2 id="字符串字面量是切片"><a class="header" href="#字符串字面量是切片">字符串字面量是切片</a></h2>
|
|
|
<p>之前提到过字符串字面量,但是没有提到它的类型:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s = "Hello, world!";
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>实际上,<code>s</code> 的类型是 <code>&str</code>,因此你也可以这样声明:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s: &str = "Hello, world!";
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>该切片指向了程序可执行文件中的某个点,这也是为什么字符串字面量是不可变的,因为 <code>&str</code> 是一个不可变引用。</p>
|
|
|
<p>了解完切片,可以进入本节的正题了。</p>
|
|
|
<h2 id="什么是字符串"><a class="header" href="#什么是字符串">什么是字符串?</a></h2>
|
|
|
<p>顾名思义,字符串是由字符组成的连续集合,但是在上一节中我们提到过,<strong>Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但是在字符串中不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)</strong>,这样有助于大幅降低字符串所占用的内存空间。</p>
|
|
|
<p>Rust 在语言级别,只有一种字符串类型: <code>str</code>,它通常是以引用类型出现 <code>&str</code>,也就是上文提到的字符串切片。虽然语言级别只有上述的 <code>str</code> 类型,但是在标准库里,还有多种不同用途的字符串类型,其中使用最广的即是 <code>String</code> 类型。</p>
|
|
|
<p><code>str</code> 类型是硬编码进可执行文件,也无法被修改,但是 <code>String</code> 则是一个可增长、可改变且具有所有权的 UTF-8 编码字符串,<strong>当 Rust 用户提到字符串时,往往指的就是 <code>String</code> 类型和 <code>&str</code> 字符串切片类型,这两个类型都是 UTF-8 编码</strong>。</p>
|
|
|
<p>除了 <code>String</code> 类型的字符串,Rust 的标准库还提供了其他类型的字符串,例如 <code>OsString</code>, <code>OsStr</code>, <code>CsString</code> 和<code> CsStr</code> 等,注意到这些名字都以 <code>String</code> 或者 <code>Str</code> 结尾了吗?它们分别对应的是具有所有权和被借用的变量。</p>
|
|
|
<h2 id="string-与-str-的转换"><a class="header" href="#string-与-str-的转换">String 与 &str 的转换</a></h2>
|
|
|
<p>在之前的代码中,已经见到好几种从 <code>&str</code> 类型生成 <code>String</code> 类型的操作:</p>
|
|
|
<ul>
|
|
|
<li><code>String::from("hello,world")</code></li>
|
|
|
<li><code>"hello,world".to_string()</code></li>
|
|
|
</ul>
|
|
|
<p>那么如何将 <code>String</code> 类型转为 <code>&str</code> 类型呢?答案很简单,取引用即可:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let s = String::from("hello,world!");
|
|
|
say_hello(&s);
|
|
|
say_hello(&s[..]);
|
|
|
say_hello(s.as_str());
|
|
|
}
|
|
|
|
|
|
fn say_hello(s: &str) {
|
|
|
println!("{}",s);
|
|
|
}</code></pre></pre>
|
|
|
<p>实际上这种灵活用法是因为 <code>deref</code> 隐式强制转换,具体我们会在 <a href="https://course.rs/advance/smart-pointer/deref.html"><code>Deref</code> 特征</a>进行详细讲解。</p>
|
|
|
<h2 id="字符串索引"><a class="header" href="#字符串索引">字符串索引</a></h2>
|
|
|
<p>在其它语言中,使用索引的方式访问字符串的某个字符或者子串是很正常的行为,但是在 Rust 中就会报错:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span> let s1 = String::from("hello");
|
|
|
let h = s1[0];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>该代码会产生如下错误:</p>
|
|
|
<pre><code class="language-console">3 | let h = s1[0];
|
|
|
| ^^^^^ `String` cannot be indexed by `{integer}`
|
|
|
|
|
|
|
= help: the trait `Index<{integer}>` is not implemented for `String`
|
|
|
</code></pre>
|
|
|
<h4 id="深入字符串内部"><a class="header" href="#深入字符串内部">深入字符串内部</a></h4>
|
|
|
<p>字符串的底层的数据存储格式实际上是[ <code>u8</code> ],一个字节数组。对于 <code>let hello = String::from("Hola");</code> 这行代码来说,<code>Hola</code> 的长度是 <code>4</code> 个字节,因为 <code>"Hola"</code> 中的每个字母在 UTF-8 编码中仅占用 1 个字节,但是对于下面的代码呢?</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let hello = String::from("中国人");
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>如果问你该字符串多长,你可能会说 <code>3</code>,但是实际上是 <code>9</code> 个字节的长度,因为大部分常用汉字在 UTF-8 中的长度是 <code>3</code> 个字节,因此这种情况下对 <code>hello</code> 进行索引,访问 <code>&hello[0]</code> 没有任何意义,因为你取不到 <code>中</code> 这个字符,而是取到了这个字符三个字节中的第一个字节,这是一个非常奇怪而且难以理解的返回值。</p>
|
|
|
<h4 id="字符串的不同表现形式"><a class="header" href="#字符串的不同表现形式">字符串的不同表现形式</a></h4>
|
|
|
<p>现在看一下用梵文写的字符串 <code>“नमस्ते”</code>, 它底层的字节数组如下形式:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
|
|
|
224, 165, 135]
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>长度是 18 个字节,这也是计算机最终存储该字符串的形式。如果从字符的形式去看,则是:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>['न', 'म', 'स', '्', 'त', 'े']
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>但是这种形式下,第四和六两个字母根本就不存在,没有任何意义,接着再从字母串的形式去看:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>["न", "म", "स्", "ते"]
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>所以,可以看出来 Rust 提供了不同的字符串展现方式,这样程序可以挑选自己想要的方式去使用,而无需去管字符串从人类语言角度看长什么样。</p>
|
|
|
<p>还有一个原因导致了 Rust 不允许去索引字符串:因为索引操作,我们总是期望它的性能表现是 O(1),然而对于 <code>String</code> 类型来说,无法保证这一点,因为 Rust 可能需要从 0 开始去遍历字符串来定位合法的字符。</p>
|
|
|
<h2 id="字符串切片"><a class="header" href="#字符串切片">字符串切片</a></h2>
|
|
|
<p>前文提到过,字符串切片是非常危险的操作,因为切片的索引是通过字节来进行,但是字符串又是 UTF-8 编码,因此你无法保证索引的字节刚好落在字符的边界上,例如:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let hello = "中国人";
|
|
|
|
|
|
let s = &hello[0..2];
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>运行上面的程序,会直接造成崩溃:</p>
|
|
|
<pre><code class="language-console">thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '中' (bytes 0..3) of `中国人`', src/main.rs:4:14
|
|
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
|
</code></pre>
|
|
|
<p>这里提示的很清楚,我们索引的字节落在了 <code>中</code> 字符的内部,这种返回没有任何意义。</p>
|
|
|
<p>因此在通过索引区间来访问字符串时,<strong>需要格外的小心</strong>,一不注意,就会导致你程序的崩溃!</p>
|
|
|
<h2 id="操作字符串"><a class="header" href="#操作字符串">操作字符串</a></h2>
|
|
|
<p>由于 <code>String</code> 是可变字符串,下面介绍 Rust 字符串的修改,添加,删除等常用方法:</p>
|
|
|
<h4 id="追加-push"><a class="header" href="#追加-push">追加 (Push)</a></h4>
|
|
|
<p>在字符串尾部可以使用 <code>push()</code> 方法追加字符 <code>char</code>,也可以使用 <code>push_str()</code> 方法追加字符串字面量。这两个方法都是<strong>在原有的字符串上追加,并不会返回新的字符串</strong>。由于字符串追加操作要修改原来的字符串,则该字符串必须是可变的,即<strong>字符串变量必须由 <code>mut</code> 关键字修饰</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut s = String::from("Hello ");
|
|
|
|
|
|
s.push_str("rust");
|
|
|
println!("追加字符串 push_str() -> {}", s);
|
|
|
|
|
|
s.push('!');
|
|
|
println!("追加字符 push() -> {}", s);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">追加字符串 push_str() -> Hello rust
|
|
|
追加字符 push() -> Hello rust!
|
|
|
</code></pre>
|
|
|
<h4 id="插入-insert"><a class="header" href="#插入-insert">插入 (Insert)</a></h4>
|
|
|
<p>可以使用 <code>insert()</code> 方法插入单个字符 <code>char</code>,也可以使用 <code>insert_str()</code> 方法插入字符串字面量,与 <code>push()</code> 方法不同,这俩方法需要传入两个参数,第一个参数是字符(串)插入位置的索引,第二个参数是要插入的字符(串),索引从 0 开始计数,如果越界则会发生错误。由于字符串插入操作要<strong>修改原来的字符串</strong>,则该字符串必须是可变的,即<strong>字符串变量必须由 <code>mut</code> 关键字修饰</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut s = String::from("Hello rust!");
|
|
|
s.insert(5, ',');
|
|
|
println!("插入字符 insert() -> {}", s);
|
|
|
s.insert_str(6, " I like");
|
|
|
println!("插入字符串 insert_str() -> {}", s);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">插入字符 insert() -> Hello, rust!
|
|
|
插入字符串 insert_str() -> Hello, I like rust!
|
|
|
</code></pre>
|
|
|
<h4 id="替换-replace"><a class="header" href="#替换-replace">替换 (Replace)</a></h4>
|
|
|
<p>如果想要把字符串中的某个字符串替换成其它的字符串,那可以使用 <code>replace()</code> 方法。与替换有关的方法有三个。</p>
|
|
|
<p>1、<code>replace</code></p>
|
|
|
<p>该方法可适用于 <code>String</code> 和 <code>&str</code> 类型。<code>replace()</code> 方法接收两个参数,第一个参数是要被替换的字符串,第二个参数是新的字符串。该方法会替换所有匹配到的字符串。<strong>该方法是返回一个新的字符串,而不是操作原来的字符串</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let string_replace = String::from("I like rust. Learning rust is my favorite!");
|
|
|
let new_string_replace = string_replace.replace("rust", "RUST");
|
|
|
dbg!(new_string_replace);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">new_string_replace = "I like RUST. Learning RUST is my favorite!"
|
|
|
</code></pre>
|
|
|
<p>2、<code>replacen</code></p>
|
|
|
<p>该方法可适用于 <code>String</code> 和 <code>&str</code> 类型。<code>replacen()</code> 方法接收三个参数,前两个参数与 <code>replace()</code> 方法一样,第三个参数则表示替换的个数。<strong>该方法是返回一个新的字符串,而不是操作原来的字符串</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let string_replace = "I like rust. Learning rust is my favorite!";
|
|
|
let new_string_replacen = string_replace.replacen("rust", "RUST", 1);
|
|
|
dbg!(new_string_replacen);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">new_string_replacen = "I like RUST. Learning rust is my favorite!"
|
|
|
</code></pre>
|
|
|
<p>3、<code>replace_range</code></p>
|
|
|
<p>该方法仅适用于 <code>String</code> 类型。<code>replace_range</code> 接收两个参数,第一个参数是要替换字符串的范围(Range),第二个参数是新的字符串。<strong>该方法是直接操作原来的字符串,不会返回新的字符串。该方法需要使用 <code>mut</code> 关键字修饰</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut string_replace_range = String::from("I like rust!");
|
|
|
string_replace_range.replace_range(7..8, "R");
|
|
|
dbg!(string_replace_range);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">string_replace_range = "I like Rust!"
|
|
|
</code></pre>
|
|
|
<h4 id="删除-delete"><a class="header" href="#删除-delete">删除 (Delete)</a></h4>
|
|
|
<p>与字符串删除相关的方法有 4 个,他们分别是 <code>pop()</code>,<code>remove()</code>,<code>truncate()</code>,<code>clear()</code>。这四个方法仅适用于 <code>String</code> 类型。</p>
|
|
|
<p>1、 <code>pop</code> —— 删除并返回字符串的最后一个字符</p>
|
|
|
<p><strong>该方法是直接操作原来的字符串</strong>。但是存在返回值,其返回值是一个 <code>Option</code> 类型,如果字符串为空,则返回 <code>None</code>。
|
|
|
示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut string_pop = String::from("rust pop 中文!");
|
|
|
let p1 = string_pop.pop();
|
|
|
let p2 = string_pop.pop();
|
|
|
dbg!(p1);
|
|
|
dbg!(p2);
|
|
|
dbg!(string_pop);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">p1 = Some(
|
|
|
'!',
|
|
|
)
|
|
|
p2 = Some(
|
|
|
'文',
|
|
|
)
|
|
|
string_pop = "rust pop 中"
|
|
|
</code></pre>
|
|
|
<p>2、 <code>remove</code> —— 删除并返回字符串中指定位置的字符</p>
|
|
|
<p><strong>该方法是直接操作原来的字符串</strong>。但是存在返回值,其返回值是删除位置的字符串,只接收一个参数,表示该字符起始索引位置。<code>remove()</code> 方法是按照字节来处理字符串的,如果参数所给的位置不是合法的字符边界,则会发生错误。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut string_remove = String::from("测试remove方法");
|
|
|
println!(
|
|
|
"string_remove 占 {} 个字节",
|
|
|
std::mem::size_of_val(string_remove.as_str())
|
|
|
);
|
|
|
// 删除第一个汉字
|
|
|
string_remove.remove(0);
|
|
|
// 下面代码会发生错误
|
|
|
// string_remove.remove(1);
|
|
|
// 直接删除第二个汉字
|
|
|
// string_remove.remove(3);
|
|
|
dbg!(string_remove);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">string_remove 占 18 个字节
|
|
|
string_remove = "试remove方法"
|
|
|
</code></pre>
|
|
|
<p>3、<code>truncate</code> —— 删除字符串中从指定位置开始到结尾的全部字符</p>
|
|
|
<p><strong>该方法是直接操作原来的字符串</strong>。无返回值。该方法 <code>truncate()</code> 方法是按照字节来处理字符串的,如果参数所给的位置不是合法的字符边界,则会发生错误。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut string_truncate = String::from("测试truncate");
|
|
|
string_truncate.truncate(3);
|
|
|
dbg!(string_truncate);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">string_truncate = "测"
|
|
|
</code></pre>
|
|
|
<p>4、<code>clear</code> —— 清空字符串</p>
|
|
|
<p><strong>该方法是直接操作原来的字符串</strong>。调用后,删除字符串中的所有字符,相当于 <code>truncate()</code> 方法参数为 0 的时候。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let mut string_clear = String::from("string clear");
|
|
|
string_clear.clear();
|
|
|
dbg!(string_clear);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">string_clear = ""
|
|
|
</code></pre>
|
|
|
<h4 id="连接-concatenate"><a class="header" href="#连接-concatenate">连接 (Concatenate)</a></h4>
|
|
|
<p>1、使用 <code>+</code> 或者 <code>+=</code> 连接字符串</p>
|
|
|
<p>使用 <code>+</code> 或者 <code>+=</code> 连接字符串,要求右边的参数必须为字符串的切片引用(Slice)类型。其实当调用 <code>+</code> 的操作符时,相当于调用了 <code>std::string</code> 标准库中的 <a href="https://doc.rust-lang.org/std/string/struct.String.html#method.add"><code>add()</code></a> 方法,这里 <code>add()</code> 方法的第二个参数是一个引用的类型。因此我们在使用 <code>+</code> 时, 必须传递切片引用类型。不能直接传递 <code>String</code> 类型。<strong><code>+</code> 是返回一个新的字符串,所以变量声明可以不需要 <code>mut</code> 关键字修饰</strong>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let string_append = String::from("hello ");
|
|
|
let string_rust = String::from("rust");
|
|
|
// &string_rust会自动解引用为&str
|
|
|
let result = string_append + &string_rust;
|
|
|
let mut result = result + "!"; // `result + "!"` 中的 `result` 是不可变的
|
|
|
result += "!!!";
|
|
|
|
|
|
println!("连接字符串 + -> {}", result);
|
|
|
}</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">连接字符串 + -> hello rust!!!!
|
|
|
</code></pre>
|
|
|
<p><code>add()</code> 方法的定义:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>fn add(self, s: &str) -> String
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>因为该方法涉及到更复杂的特征功能,因此我们这里简单说明下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let s1 = String::from("hello,");
|
|
|
let s2 = String::from("world!");
|
|
|
// 在下句中,s1的所有权被转移走了,因此后面不能再使用s1
|
|
|
let s3 = s1 + &s2;
|
|
|
assert_eq!(s3,"hello,world!");
|
|
|
// 下面的语句如果去掉注释,就会报错
|
|
|
// println!("{}",s1);
|
|
|
}</code></pre></pre>
|
|
|
<p><code>self</code> 是 <code>String</code> 类型的字符串 <code>s1</code>,该函数说明,只能将 <code>&str</code> 类型的字符串切片添加到 <code>String</code> 类型的 <code>s1</code> 上,然后返回一个新的 <code>String</code> 类型,所以 <code>let s3 = s1 + &s2;</code> 就很好解释了,将 <code>String</code> 类型的 <code>s1</code> 与 <code>&str</code> 类型的 <code>s2</code> 进行相加,最终得到 <code>String</code> 类型的 <code>s3</code>。</p>
|
|
|
<p>由此可推,以下代码也是合法的:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>let s1 = String::from("tic");
|
|
|
let s2 = String::from("tac");
|
|
|
let s3 = String::from("toe");
|
|
|
|
|
|
// String = String + &str + &str + &str + &str
|
|
|
let s = s1 + "-" + &s2 + "-" + &s3;
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p><code>String + &str</code>返回一个 <code>String</code>,然后再继续跟一个 <code>&str</code> 进行 <code>+</code> 操作,返回一个 <code>String</code> 类型,不断循环,最终生成一个 <code>s</code>,也是 <code>String</code> 类型。</p>
|
|
|
<p><code>s1</code> 这个变量通过调用 <code>add()</code> 方法后,所有权被转移到 <code>add()</code> 方法里面, <code>add()</code> 方法调用后就被释放了,同时 <code>s1</code> 也被释放了。再使用 <code>s1</code> 就会发生错误。这里涉及到<a href="https://course.rs/basic/ownership/ownership.html#%E8%BD%AC%E7%A7%BB%E6%89%80%E6%9C%89%E6%9D%83">所有权转移(Move)</a>的相关知识。</p>
|
|
|
<p>2、使用 <code>format!</code> 连接字符串</p>
|
|
|
<p><code>format!</code> 这种方式适用于 <code>String</code> 和 <code>&str</code> 。<code>format!</code> 的用法与 <code>print!</code> 的用法类似,详见<a href="https://course.rs/basic/formatted-output.html#printprintlnformat">格式化输出</a>。</p>
|
|
|
<p>示例代码如下:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
let s1 = "hello";
|
|
|
let s2 = String::from("rust");
|
|
|
let s = format!("{} {}!", s1, s2);
|
|
|
println!("{}", s);
|
|
|
}
|
|
|
</code></pre></pre>
|
|
|
<p>代码运行结果:</p>
|
|
|
<pre><code class="language-console">hello rust!
|
|
|
</code></pre>
|
|
|
<h2 id="字符串转义"><a class="header" href="#字符串转义">字符串转义</a></h2>
|
|
|
<p>我们可以通过转义的方式 <code>\</code> 输出 ASCII 和 Unicode 字符。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
// 通过 \ + 字符的十六进制表示,转义输出一个字符
|
|
|
let byte_escape = "I'm writing \x52\x75\x73\x74!";
|
|
|
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
|
|
|
|
|
|
// \u 可以输出一个 unicode 字符
|
|
|
let unicode_codepoint = "\u{211D}";
|
|
|
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
|
|
|
|
|
|
println!(
|
|
|
"Unicode character {} (U+211D) is called {}",
|
|
|
unicode_codepoint, character_name
|
|
|
);
|
|
|
|
|
|
// 换行了也会保持之前的字符串格式
|
|
|
// 使用\忽略换行符
|
|
|
let long_string = "String literals
|
|
|
can span multiple lines.
|
|
|
The linebreak and indentation here ->\
|
|
|
<- can be escaped too!";
|
|
|
println!("{}", long_string);
|
|
|
}</code></pre></pre>
|
|
|
<p>当然,在某些情况下,可能你会希望保持字符串的原样,不要转义:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
println!("{}", "hello \\x52\\x75\\x73\\x74");
|
|
|
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
|
|
|
println!("{}", raw_str);
|
|
|
|
|
|
// 如果字符串包含双引号,可以在开头和结尾加 #
|
|
|
let quotes = r#"And then I said: "There is no escape!""#;
|
|
|
println!("{}", quotes);
|
|
|
|
|
|
// 如果还是有歧义,可以继续增加,没有限制
|
|
|
let longer_delimiter = r###"A string with "# in it. And even "##!"###;
|
|
|
println!("{}", longer_delimiter);
|
|
|
}</code></pre></pre>
|
|
|
<h2 id="操作-utf-8-字符串"><a class="header" href="#操作-utf-8-字符串">操作 UTF-8 字符串</a></h2>
|
|
|
<p>前文提到了几种使用 UTF-8 字符串的方式,下面来一一说明。</p>
|
|
|
<h4 id="字符"><a class="header" href="#字符">字符</a></h4>
|
|
|
<p>如果你想要以 Unicode 字符的方式遍历字符串,最好的办法是使用 <code>chars</code> 方法,例如:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>for c in "中国人".chars() {
|
|
|
println!("{}", c);
|
|
|
}
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>输出如下</p>
|
|
|
<pre><code class="language-console">中
|
|
|
国
|
|
|
人
|
|
|
</code></pre>
|
|
|
<h4 id="字节"><a class="header" href="#字节">字节</a></h4>
|
|
|
<p>这种方式是返回字符串的底层字节数组表现形式:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>for b in "中国人".bytes() {
|
|
|
println!("{}", b);
|
|
|
}
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>输出如下:</p>
|
|
|
<pre><code class="language-console">228
|
|
|
184
|
|
|
173
|
|
|
229
|
|
|
155
|
|
|
189
|
|
|
228
|
|
|
186
|
|
|
186
|
|
|
</code></pre>
|
|
|
<h4 id="获取子串"><a class="header" href="#获取子串">获取子串</a></h4>
|
|
|
<p>想要准确的从 UTF-8 字符串中获取子串是较为复杂的事情,例如想要从 <code>holla中国人नमस्ते</code> 这种变长的字符串中取出某一个子串,使用标准库你是做不到的。
|
|
|
你需要在 <code>crates.io</code> 上搜索 <code>utf8</code> 来寻找想要的功能。</p>
|
|
|
<p>可以考虑尝试下这个库:<a href="https://crates.io/crates/utf8_slice">utf8_slice</a>。</p>
|
|
|
<h2 id="字符串深度剖析"><a class="header" href="#字符串深度剖析">字符串深度剖析</a></h2>
|
|
|
<p>那么问题来了,为啥 <code>String</code> 可变,而字符串字面值 <code>str</code> 却不可以?</p>
|
|
|
<p>就字符串字面值来说,我们在编译时就知道其内容,最终字面值文本被直接硬编码进可执行文件中,这使得字符串字面值快速且高效,这主要得益于字符串字面值的不可变性。不幸的是,我们不能为了获得这种性能,而把每一个在编译时大小未知的文本都放进内存中(你也做不到!),因为有的字符串是在程序运行得过程中动态生成的。</p>
|
|
|
<p>对于 <code>String</code> 类型,为了支持一个可变、可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容,这些都是在程序运行时完成的:</p>
|
|
|
<ul>
|
|
|
<li>首先向操作系统请求内存来存放 <code>String</code> 对象</li>
|
|
|
<li>在使用完成后,将内存释放,归还给操作系统</li>
|
|
|
</ul>
|
|
|
<p>其中第一部分由 <code>String::from</code> 完成,它创建了一个全新的 <code>String</code>。</p>
|
|
|
<p>重点来了,到了第二部分,就是百家齐放的环节,在有<strong>垃圾回收 GC</strong> 的语言中,GC 来负责标记并清除这些不再使用的内存对象,这个过程都是自动完成,无需开发者关心,非常简单好用;但是在无 GC 的语言中,需要开发者手动去释放这些内存对象,就像创建对象需要通过编写代码来完成一样,未能正确释放对象造成的后果简直不可估量。</p>
|
|
|
<p>对于 Rust 而言,安全和性能是写到骨子里的核心特性,如果使用 GC,那么会牺牲性能;如果使用手动管理内存,那么会牺牲安全,这该怎么办?为此,Rust 的开发者想出了一个无比惊艳的办法:变量在离开作用域后,就自动释放其占用的内存:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>{
|
|
|
let s = String::from("hello"); // 从此处起,s 是有效的
|
|
|
|
|
|
// 使用 s
|
|
|
} // 此作用域已结束,
|
|
|
// s 不再有效,内存被释放
|
|
|
<span class="boring">}</span></code></pre></pre>
|
|
|
<p>与其它系统编程语言的 <code>free</code> 函数相同,Rust 也提供了一个释放内存的函数: <code>drop</code>,但是不同的是,其它语言要手动调用 <code>free</code> 来释放每一个变量占用的内存,而 Rust 则在变量离开作用域时,自动调用 <code>drop</code> 函数: 上面代码中,Rust 在结尾的 <code>}</code> 处自动调用 <code>drop</code>。</p>
|
|
|
<blockquote>
|
|
|
<p>其实,在 C++ 中,也有这种概念: <em>Resource Acquisition Is Initialization (RAII)</em>。如果你使用过 RAII 模式的话应该对 Rust 的 <code>drop</code> 函数并不陌生。</p>
|
|
|
</blockquote>
|
|
|
<p>这个模式对编写 Rust 代码的方式有着深远的影响,在后面章节我们会进行更深入的介绍。</p>
|
|
|
<h2 id="课后练习"><a class="header" href="#课后练习">课后练习</a></h2>
|
|
|
<blockquote>
|
|
|
<p>Rust By Practice,支持代码在线编辑和运行,并提供详细的习题解答。</p>
|
|
|
<ul>
|
|
|
<li><a href="https://practice-zh.course.rs/compound-types/string.html">字符串</a>
|
|
|
<ul>
|
|
|
<li><a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/string.md">习题解答</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li><a href="https://practice-zh.course.rs/compound-types/slice.html">切片</a>
|
|
|
<ul>
|
|
|
<li><a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/compound-types/slice.md">习题解答</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
<li><a href="https://practice-zh.course.rs/collections/String.html">String</a>
|
|
|
<ul>
|
|
|
<li><a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/collections/String.md">习题解答</a></li>
|
|
|
</ul>
|
|
|
</li>
|
|
|
</ul>
|
|
|
</blockquote>
|
|
|
<hr />
|
|
|
<h2 id="引用资料"><a class="header" href="#引用资料">引用资料</a></h2>
|
|
|
<ol>
|
|
|
<li>https://blog.csdn.net/a1595901624/article/details/119294443</li>
|
|
|
</ol>
|
|
|
|
|
|
<div id="giscus-container"></div>
|
|
|
</main>
|
|
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
|
<!-- Mobile navigation buttons -->
|
|
|
<a rel="prev" href="../../basic/compound-type/intro.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next prefetch" href="../../basic/compound-type/tuple.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
|
</nav>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
<a rel="prev" href="../../basic/compound-type/intro.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next prefetch" href="../../basic/compound-type/tuple.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
window.playground_copyable = true;
|
|
|
</script>
|
|
|
|
|
|
<script src="../../ace.js"></script>
|
|
|
<script src="../../editor.js"></script>
|
|
|
<script src="../../mode-rust.js"></script>
|
|
|
<script src="../../theme-dawn.js"></script>
|
|
|
<script src="../../theme-tomorrow_night.js"></script>
|
|
|
|
|
|
<script src="../../elasticlunr.min.js"></script>
|
|
|
<script src="../../mark.min.js"></script>
|
|
|
<script src="../../searcher.js"></script>
|
|
|
|
|
|
<script src="../../clipboard.min.js"></script>
|
|
|
<script src="../../highlight.js"></script>
|
|
|
<script src="../../book.js"></script>
|
|
|
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
var pagePath = "basic/compound-type/string-slice.md"
|
|
|
</script>
|
|
|
|
|
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
<script src="../../assets/custom.js"></script>
|
|
|
<script src="../../assets/bigPicture.js"></script>
|
|
|
|
|
|
|
|
|
</div>
|
|
|
</body>
|
|
|
</html> |