├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── ch01 └── mod.rs ├── ch10 ├── README.md ├── csv_challenge │ ├── .gitignore │ ├── Cargo.toml │ ├── README.md │ ├── benches │ │ └── file_op_bench.rs │ ├── input │ │ └── challenge.csv │ ├── output │ │ └── output.csv │ ├── src │ │ ├── core.rs │ │ ├── core │ │ │ ├── read.rs │ │ │ └── write.rs │ │ ├── err.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ └── opt.rs │ └── tests │ │ └── integration_test.rs ├── mod.rs ├── phrases │ ├── Cargo.toml │ └── src │ │ ├── chinese.rs │ │ ├── chinese │ │ ├── farewells.rs │ │ └── greetings.rs │ │ ├── english │ │ ├── farewells.rs │ │ ├── greetings.rs │ │ └── mod.rs │ │ ├── lib.rs │ │ └── main.rs ├── static_hashmap │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── main.rs └── use_regex │ ├── Cargo.toml │ ├── README.md │ └── src │ └── main.rs ├── ch11 ├── await │ ├── Cargo.toml │ └── src │ │ └── main.rs └── mod.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 3 | # Edit at https://www.gitignore.io/?templates=vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 4 | 5 | ### Eclipse ### 6 | .metadata 7 | bin/ 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .settings/ 15 | .loadpath 16 | .recommenders 17 | 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # PyDev specific (Python IDE for Eclipse) 25 | *.pydevproject 26 | 27 | # CDT-specific (C/C++ Development Tooling) 28 | .cproject 29 | 30 | # CDT- autotools 31 | .autotools 32 | 33 | # Java annotation processor (APT) 34 | .factorypath 35 | 36 | # PDT-specific (PHP Development Tools) 37 | .buildpath 38 | 39 | # sbteclipse plugin 40 | .target 41 | 42 | # Tern plugin 43 | .tern-project 44 | 45 | # TeXlipse plugin 46 | .texlipse 47 | 48 | # STS (Spring Tool Suite) 49 | .springBeans 50 | 51 | # Code Recommenders 52 | .recommenders/ 53 | 54 | # Annotation Processing 55 | .apt_generated/ 56 | 57 | # Scala IDE specific (Scala & Java development for Eclipse) 58 | .cache-main 59 | .scala_dependencies 60 | .worksheet 61 | 62 | ### Eclipse Patch ### 63 | # Eclipse Core 64 | .project 65 | 66 | # JDT-specific (Eclipse Java Development Tools) 67 | .classpath 68 | 69 | # Annotation Processing 70 | .apt_generated 71 | 72 | .sts4-cache/ 73 | 74 | ### GitBook ### 75 | # Node rules: 76 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 77 | .grunt 78 | 79 | ## Dependency directory 80 | ## Commenting this out is preferred by some people, see 81 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 82 | node_modules 83 | 84 | # Book build output 85 | _book 86 | 87 | # eBook build output 88 | *.epub 89 | *.mobi 90 | *.pdf 91 | 92 | ### Go ### 93 | # Binaries for programs and plugins 94 | *.exe 95 | *.exe~ 96 | *.dll 97 | *.so 98 | *.dylib 99 | 100 | # Test binary, built with `go test -c` 101 | *.test 102 | 103 | # Output of the go coverage tool, specifically when used with LiteIDE 104 | *.out 105 | 106 | # Dependency directories (remove the comment below to include it) 107 | # vendor/ 108 | 109 | ### Go Patch ### 110 | /vendor/ 111 | /Godeps/ 112 | 113 | ### Java ### 114 | # Compiled class file 115 | *.class 116 | 117 | # Log file 118 | *.log 119 | 120 | # BlueJ files 121 | *.ctxt 122 | 123 | # Mobile Tools for Java (J2ME) 124 | .mtj.tmp/ 125 | 126 | # Package Files # 127 | *.jar 128 | *.war 129 | *.nar 130 | *.ear 131 | *.zip 132 | *.tar.gz 133 | *.rar 134 | 135 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 136 | hs_err_pid* 137 | 138 | ### JetBrains+all ### 139 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 140 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 141 | 142 | # User-specific stuff 143 | .idea/**/workspace.xml 144 | .idea/**/tasks.xml 145 | .idea/**/usage.statistics.xml 146 | .idea/**/dictionaries 147 | .idea/**/shelf 148 | 149 | # Generated files 150 | .idea/**/contentModel.xml 151 | 152 | # Sensitive or high-churn files 153 | .idea/**/dataSources/ 154 | .idea/**/dataSources.ids 155 | .idea/**/dataSources.local.xml 156 | .idea/**/sqlDataSources.xml 157 | .idea/**/dynamic.xml 158 | .idea/**/uiDesigner.xml 159 | .idea/**/dbnavigator.xml 160 | 161 | # Gradle 162 | .idea/**/gradle.xml 163 | .idea/**/libraries 164 | 165 | # Gradle and Maven with auto-import 166 | # When using Gradle or Maven with auto-import, you should exclude module files, 167 | # since they will be recreated, and may cause churn. Uncomment if using 168 | # auto-import. 169 | # .idea/modules.xml 170 | # .idea/*.iml 171 | # .idea/modules 172 | # *.iml 173 | # *.ipr 174 | 175 | # CMake 176 | cmake-build-*/ 177 | 178 | # Mongo Explorer plugin 179 | .idea/**/mongoSettings.xml 180 | 181 | # File-based project format 182 | *.iws 183 | 184 | # IntelliJ 185 | out/ 186 | 187 | # mpeltonen/sbt-idea plugin 188 | .idea_modules/ 189 | 190 | # JIRA plugin 191 | atlassian-ide-plugin.xml 192 | 193 | # Cursive Clojure plugin 194 | .idea/replstate.xml 195 | 196 | # Crashlytics plugin (for Android Studio and IntelliJ) 197 | com_crashlytics_export_strings.xml 198 | crashlytics.properties 199 | crashlytics-build.properties 200 | fabric.properties 201 | 202 | # Editor-based Rest Client 203 | .idea/httpRequests 204 | 205 | # Android studio 3.1+ serialized cache file 206 | .idea/caches/build_file_checksums.ser 207 | 208 | ### JetBrains+all Patch ### 209 | # Ignores the whole .idea folder and all .iml files 210 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 211 | 212 | .idea/ 213 | 214 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 215 | 216 | *.iml 217 | modules.xml 218 | .idea/misc.xml 219 | *.ipr 220 | 221 | # Sonarlint plugin 222 | .idea/sonarlint 223 | 224 | ### Linux ### 225 | *~ 226 | 227 | # temporary files which can be created if a process still has a handle open of a deleted file 228 | .fuse_hidden* 229 | 230 | # KDE directory preferences 231 | .directory 232 | 233 | # Linux trash folder which might appear on any partition or disk 234 | .Trash-* 235 | 236 | # .nfs files are created when an open file is removed but is still being accessed 237 | .nfs* 238 | 239 | ### macOS ### 240 | # General 241 | .DS_Store 242 | .AppleDouble 243 | .LSOverride 244 | 245 | # Icon must end with two \r 246 | Icon 247 | 248 | # Thumbnails 249 | ._* 250 | 251 | # Files that might appear in the root of a volume 252 | .DocumentRevisions-V100 253 | .fseventsd 254 | .Spotlight-V100 255 | .TemporaryItems 256 | .Trashes 257 | .VolumeIcon.icns 258 | .com.apple.timemachine.donotpresent 259 | 260 | # Directories potentially created on remote AFP share 261 | .AppleDB 262 | .AppleDesktop 263 | Network Trash Folder 264 | Temporary Items 265 | .apdisk 266 | 267 | ### Maven ### 268 | target/ 269 | pom.xml.tag 270 | pom.xml.releaseBackup 271 | pom.xml.versionsBackup 272 | pom.xml.next 273 | release.properties 274 | dependency-reduced-pom.xml 275 | buildNumber.properties 276 | .mvn/timing.properties 277 | .mvn/wrapper/maven-wrapper.jar 278 | .flattened-pom.xml 279 | 280 | ### OSX ### 281 | # General 282 | 283 | # Icon must end with two \r 284 | 285 | # Thumbnails 286 | 287 | # Files that might appear in the root of a volume 288 | 289 | # Directories potentially created on remote AFP share 290 | 291 | ### Rust ### 292 | # Generated by Cargo 293 | # will have compiled files and executables 294 | /target/ 295 | 296 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 297 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 298 | Cargo.lock 299 | 300 | # These are backup files generated by rustfmt 301 | **/*.rs.bk 302 | 303 | ### Vim ### 304 | # Swap 305 | [._]*.s[a-v][a-z] 306 | [._]*.sw[a-p] 307 | [._]s[a-rt-v][a-z] 308 | [._]ss[a-gi-z] 309 | [._]sw[a-p] 310 | 311 | # Session 312 | Session.vim 313 | Sessionx.vim 314 | 315 | # Temporary 316 | .netrwhist 317 | # Auto-generated tag files 318 | tags 319 | # Persistent undo 320 | [._]*.un~ 321 | 322 | ### VisualStudioCode ### 323 | .vscode/* 324 | !.vscode/settings.json 325 | !.vscode/tasks.json 326 | !.vscode/launch.json 327 | !.vscode/extensions.json 328 | 329 | ### VisualStudioCode Patch ### 330 | # Ignore all local history of files 331 | .history 332 | 333 | ### Windows ### 334 | # Windows thumbnail cache files 335 | Thumbs.db 336 | Thumbs.db:encryptable 337 | ehthumbs.db 338 | ehthumbs_vista.db 339 | 340 | # Dump file 341 | *.stackdump 342 | 343 | # Folder config file 344 | [Dd]esktop.ini 345 | 346 | # Recycle Bin used on file shares 347 | $RECYCLE.BIN/ 348 | 349 | # Windows Installer files 350 | *.cab 351 | *.msi 352 | *.msix 353 | *.msm 354 | *.msp 355 | 356 | # Windows shortcuts 357 | *.lnk 358 | 359 | # End of https://www.gitignore.io/api/vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 360 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-programming-codes" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | definitions = "《Rust编程之道》学习笔记及代码" 7 | homepage = "https://github.com/tonydeng/rust-programming-codes" 8 | license = "MIT" 9 | keywords = ["book","rust"] 10 | categories = ["rust-programming-book-codes"] 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tony Deng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonydeng/rust-programming-codes/6e4b18e89cb64aab5c8fde76619b01579b24d4ac/README.md -------------------------------------------------------------------------------- /src/ch01/mod.rs: -------------------------------------------------------------------------------- 1 | //! 第一章 新时代的语言 2 | //! 3 | 4 | 5 | /// # Example 6 | /// 7 | /// Basic usage: 8 | /// 9 | /// ``` 10 | /// pub fn title() { 11 | /// println!("第一章: {}", "新时代的语言"); 12 | /// } 13 | /// title(); 14 | /// ``` 15 | pub fn title(){ 16 | println!("第一章: {}", "新时代的语言"); 17 | } 18 | 19 | 20 | // 定义结构体 21 | pub struct Duck; 22 | pub struct Pig; 23 | 24 | // 定义Fly接口 25 | pub trait Fly { 26 | // 定义fly函数 27 | fn fly(&self) -> bool; 28 | } 29 | 30 | // Duck实现Fly接口 31 | impl Fly for Duck { 32 | // Duck可以飞 33 | fn fly(&self) -> bool { 34 | return true; 35 | } 36 | } 37 | 38 | // Pig实现Fly接口 39 | impl Fly for Pig { 40 | // Pig不能飞 41 | fn fly(&self) -> bool { 42 | return false; 43 | } 44 | } 45 | /// # 零成本抽象 fly_static 46 | /// 47 | /// ``` 48 | /// struct Duck; 49 | /// struct Pig; 50 | /// 51 | /// trait Fly { 52 | /// fn fly(&self) -> bool; 53 | /// } 54 | /// 55 | /// impl Fly for Duck { 56 | /// fn fly(&self) -> bool { 57 | /// return true; 58 | /// } 59 | /// } 60 | /// impl Fly for Pig { 61 | /// fn fly(&self) -> bool { 62 | /// return false; 63 | /// } 64 | /// } 65 | /// 66 | /// fn fly_static(s: T) -> bool { 67 | /// s.fly() 68 | /// } 69 | /// 70 | /// fn fly_dyn(s: &Fly) -> bool { 71 | /// s.fly() 72 | /// } 73 | /// let pig = Pig; 74 | /// assert_eq!(fly_static::(pig), false); 75 | /// let duck = Duck; 76 | /// assert_eq!(fly_static::(duck), true); 77 | /// ``` 78 | pub fn fiy_static(s: T) -> bool { 79 | s.fly() 80 | } 81 | 82 | /// # 零成本抽象 fly_dyn 83 | /// 84 | /// ``` 85 | /// struct Duck; 86 | /// struct Pig; 87 | /// 88 | /// trait Fly { 89 | /// fn fly(&self) -> bool; 90 | /// } 91 | /// 92 | /// impl Fly for Duck { 93 | /// fn fly(&self) -> bool { 94 | /// return true; 95 | /// } 96 | /// } 97 | /// impl Fly for Pig { 98 | /// fn fly(&self) -> bool { 99 | /// return false; 100 | /// } 101 | /// } 102 | /// 103 | /// fn fly_static(s: T) -> bool { 104 | /// s.fly() 105 | /// } 106 | /// 107 | /// fn fly_dyn(s: &Fly) -> bool { 108 | /// s.fly() 109 | /// } 110 | /// assert_eq!(fly_dyn(&Pig), false); 111 | /// assert_eq!(fly_dyn(&Duck), true); 112 | /// ``` 113 | pub fn fiy_dyn(s: &Fly) -> bool { 114 | s.fly() 115 | } -------------------------------------------------------------------------------- /src/ch10/README.md: -------------------------------------------------------------------------------- 1 | # 包装箱和模块 2 | 3 | - [包装箱和模块](#%e5%8c%85%e8%a3%85%e7%ae%b1%e5%92%8c%e6%a8%a1%e5%9d%97) 4 | - [基础术语:包装箱和模块](#%e5%9f%ba%e7%a1%80%e6%9c%af%e8%af%ad%e5%8c%85%e8%a3%85%e7%ae%b1%e5%92%8c%e6%a8%a1%e5%9d%97) 5 | - [多文件包装箱](#%e5%a4%9a%e6%96%87%e4%bb%b6%e5%8c%85%e8%a3%85%e7%ae%b1) 6 | - [导入外部的包装箱](#%e5%af%bc%e5%85%a5%e5%a4%96%e9%83%a8%e7%9a%84%e5%8c%85%e8%a3%85%e7%ae%b1) 7 | - [导出公用接口](#%e5%af%bc%e5%87%ba%e5%85%ac%e7%94%a8%e6%8e%a5%e5%8f%a3) 8 | - [用`use`导入模块](#%e7%94%a8use%e5%af%bc%e5%85%a5%e6%a8%a1%e5%9d%97) 9 | - [使用`pub use`重导出](#%e4%bd%bf%e7%94%a8pub-use%e9%87%8d%e5%af%bc%e5%87%ba) 10 | - [复杂的导入](#%e5%a4%8d%e6%9d%82%e7%9a%84%e5%af%bc%e5%85%a5) 11 | - [代码示例](#%e4%bb%a3%e7%a0%81%e7%a4%ba%e4%be%8b) 12 | - [扩展阅读](#%e6%89%a9%e5%b1%95%e9%98%85%e8%af%bb) 13 | 14 | 当一个项目变大以后,良好的软件工程实践是把它分为一堆较小的部分,再把它们装配到一起。定义良好的接口也非常重要,以使有些功能是私有的,而有些是公有的。`Rust`有一个模块系统来帮助我们处理这些工作。 15 | 16 | ## 基础术语:包装箱和模块 17 | 18 | `Rust`有两个不同的术语与模块系统有关:包装箱(`crate`)和模块(`module`)。包装箱是其它语言中库(`library`)或包(`package`)的同义词。因此,“`Cargo`”则是`Rust`包管理工具的名字:你通过`Cargo`把你的包装箱交付给别人。包装箱可以根据项目的不同,生成可执行文件或库文件。 19 | 20 | 每个包装箱有一个隐含的根模块(`root module`)包含了该包装箱的代码。你可以在根模块下定义一个子模块树。模块让你可以在包装箱内部为代码分区。 21 | 22 | 作为一个例子,让我们来创建一个短语(`phrases`)包装箱,它会给我们一些不同语言的短语。为了简单起见,仅有“你好”和“再见”这两种短语,并使用英语和日语作为这些短语的语言。我们采用如下模块布局: 23 | 24 | ```text 25 | +-----------+ 26 | +---| greetings | 27 | +---------+ | +-----------+ 28 | +---| english |---+ 29 | | +---------+ | +-----------+ 30 | | +---| farewells | 31 | +---------+ | +-----------+ 32 | | phrases |---+ 33 | +---------+ | +-----------+ 34 | | +---| greetings | 35 | | +----------+ | +-----------+ 36 | +---| japanese |--+ 37 | +----------+ | +-----------+ 38 | +---| farewells | 39 | +-----------+ 40 | ``` 41 | 42 | 在这个例子中,`phrases`是我们包装箱的名字。剩下所有的都是模块。你可以看到它们组成了一个树,以包装箱为根(即`phrases`树的根)分叉出来。 43 | 现在我们想要在代码中定义这些模块。首先,用`Cargo`创建一个新包装箱: 44 | 45 | ```bash 46 | $ cargo new phrases --lib 47 | $ cd phrases 48 | ``` 49 | 50 | 如果你还记得,这会生成一个简单的项目: 51 | 52 | ```bash 53 | $ tree . 54 | . 55 | ├── Cargo.toml 56 | └── src 57 | └── lib.rs 58 | 1 directory, 2 files 59 | ``` 60 | 61 | `src/lib.rs`是我们包装箱的根,与上面图表中的`phrases`对应。 62 | 定义模块 63 | 我们用`mod`关键字来定义我们的每一个模块。让我们把`src/lib.rs`写成这样: 64 | 65 | ```rust 66 | mod english { 67 | mod greetings { 68 | } 69 | 70 | mod farewells { 71 | } 72 | } 73 | 74 | mod japanese { 75 | mod greetings { 76 | } 77 | 78 | mod farewells { 79 | } 80 | } 81 | ``` 82 | 83 | 在`mod`关键字之后是模块的名字。模块的命名采用`Rust`其它标识符的命名惯例:`lower_snake_case`。在大括号中(`{}`)是模块的内容。 84 | 85 | 在`mod`中,你可以定义子`mod`。我们可以用双冒号(`::`)标记访问子模块。我们的4个嵌套模块是`english::greetings`,`english::farewells`,`japanese::greetings`和`japanese::farewells`。 86 | 87 | 因为子模块位于父模块的命名空间中,所以这些不会冲突:`english::greetings`和`japanese::greetings`是不同的,即便它们的名字都是`greetings`。 88 | 89 | 因为这个包装箱的根文件叫做`lib.rs`,且没有一个`main()`函数。`Cargo`会把这个包装箱构建为一个库: 90 | 91 | ```bash 92 | $ cargo build 93 | Compiling phrases v0.0.1 (file:///home/you/projects/phrases) 94 | $ ls target/debug 95 | build deps examples libphrases-a7448e02a0468eaa.rlib native 96 | ``` 97 | 98 | `libphrases-.rlib`是构建好的包装箱。在我们了解如何使用这个包装箱之前,先让我们把它拆分为多个文件。 99 | 100 | ## 多文件包装箱 101 | 102 | 如果每个包装箱只能有一个文件,这些文件将会变得非常庞大。把包装箱分散到多个文件也非常简单,`Rust`支持两种方法。 103 | 104 | 除了这样定义一个模块外: 105 | 106 | ```rust 107 | mod english { 108 | // Contents of our module go here. 109 | } 110 | ``` 111 | 112 | 我们还可以这样定义: 113 | 114 | ```rust 115 | mod english; 116 | ``` 117 | 118 | 如果我们这么做的话,`Rust`会期望能找到一个包含我们模块内容的`english.rs`文件,或者包含我们模块内容的`english/mod.rs`文件: 119 | 120 | > 注意在这些文件中,你不需要重新定义这些模块:它们已经由最开始的`mod`定义。 121 | 122 | 使用这两个技巧,我们可以将我们的包装箱拆分为两个目录和七个文件: 123 | 124 | ```bash 125 | $ tree . 126 | . 127 | ├── Cargo.lock 128 | ├── Cargo.toml 129 | ├── src 130 | │ ├── english 131 | │ │ ├── farewells.rs 132 | │ │ ├── greetings.rs 133 | │ │ └── mod.rs 134 | │ ├── japanese 135 | │ │ ├── farewells.rs 136 | │ │ ├── greetings.rs 137 | │ │ └── mod.rs 138 | │ └── lib.rs 139 | └── target 140 | └── debug 141 | ├── build 142 | ├── deps 143 | ├── examples 144 | ├── libphrases-a7448e02a0468eaa.rlib 145 | └── native 146 | ``` 147 | 148 | `src/lib.rs`是我们包装箱的根,它看起来像这样: 149 | 150 | ```rust 151 | mod english; 152 | mod japanese; 153 | ``` 154 | 155 | 这两个定义告诉`Rust`去寻找 156 | 157 | - `src/english.rs`或`src/english/mod.rs` 158 | - `src/japanese.rs`或`src/japanese/mod.rs` 159 | 160 | 具体根据你的偏好。在我们的例子中,因为我们的模块含有子模块,所以我们选择第二种方式。`src/english/mod.rs`和`src/japanese/mod.rs`都看起来像这样: 161 | 162 | ```rust 163 | mod greetings; 164 | mod farewells; 165 | ``` 166 | 167 | 再一次,这些定义告诉`Rust`去寻找 168 | 169 | - `src/english/greetings.rs`或`src/english/greetings/mod.rs` 170 | - `src/english/farewells.rs`或`src/english/farewells/mod.rs` 171 | - `src/japanese/greetings.rs`或`src/japanese/greetings/mod.rs` 172 | - `src/japanese/farewells.rs`或`src/japanese/farewells/mod.rs` 173 | 174 | 因为这些子模块没有自己的子模块,我们选择`src/english/greetings.rs`和`src/japanese/farewells.rs`。 175 | 176 | 现在`src/english/greetings.rs`和`src/japanese/farewells.rs`都是空的。让我们添加一些函数。 177 | 178 | 在`src/english/greetings.rs`添加如下: 179 | 180 | ```rust 181 | fn hello() -> String { 182 | "Hello!".to_string() 183 | } 184 | ``` 185 | 186 | 在`src/english/farewells.rs`添加如下: 187 | 188 | ```rust 189 | fn goodbye() -> String { 190 | "Goodbye.".to_string() 191 | } 192 | ``` 193 | 194 | 在src/japanese/greetings.rs添加如下: 195 | 196 | ```rust 197 | fn hello() -> String { 198 | "こんにちは".to_string() 199 | } 200 | ``` 201 | 202 | 当然,你可以从本文复制粘贴这些内容,或者写点别的东西。事实上你写进去“konnichiwa”对我们学习模块系统并不重要。 203 | 204 | 在`src/japanese/farewells.rs`添加如下: 205 | 206 | ```rust 207 | fn goodbye() -> String { 208 | "さようなら".to_string() 209 | } 210 | ``` 211 | 212 | (这是“Sayōnara”,如果你很好奇的话。) 213 | 现在我们在包装箱中添加了一些函数,让我们尝试在别的包装箱中使用它。 214 | 215 | ## 导入外部的包装箱 216 | 217 | 我们有了一个库包装箱。让我们创建一个可执行的包装箱来导入和使用我们的库。 218 | 219 | 创建一个`src/main.rs`文件然后写入如下:(现在它还不能编译) 220 | 221 | ```rust 222 | extern crate phrases; 223 | 224 | fn main() { 225 | println!("Hello in English: {}", phrases::english::greetings::hello()); 226 | println!("Goodbye in English: {}", phrases::english::farewells::goodbye()); 227 | 228 | println!("Hello in Japanese: {}", phrases::japanese::greetings::hello()); 229 | println!("Goodbye in Japanese: {}", phrases::japanese::farewells::goodbye()); 230 | } 231 | ``` 232 | 233 | `extern crate`声明告诉`Rust`我们需要编译和链接`phrases`包装箱。然后我们就可以在这里使用`phrases`的模块了。就像我们之前提到的,你可以用双冒号引用子模块和之中的函数。 234 | (注意:当导入像“`like-this`”名字中包含连字符的`crate`时,这样的名字并不是一个有效的 `Rust`标识符,它可以通过将连字符变为下划线来转换,所以你应该写成`extern crate like_this;`) 235 | 236 | 另外,`Cargo`假设`src/main.rs`是二进制包装箱的根,而不是库包装箱的。现在我们的包中有两个包装箱:`src/lib.rs`和`src/main.rs`。这种模式在可执行包装箱中非常常见:大部分功能都在库包装箱中,而可执行包装箱使用这个库。这样,其它程序可以只使用我们的库,另外这也是各司其职的良好分离。 237 | 238 | 现在它还不能很好的工作。我们会得到 4 个错误,它们看起来像: 239 | 240 | ```bash 241 | $ cargo build 242 | Compiling phrases v0.0.1 (file:///home/you/projects/phrases) 243 | src/main.rs:4:38: 4:72 error: function `hello` is private 244 | src/main.rs:4 println!("Hello in English: {}", phrases::english::greetings::hello()); 245 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 246 | note: in expansion of format_args! 247 | :2:25: 2:58 note: expansion site 248 | :1:1: 2:62 note: in expansion of print! 249 | :3:1: 3:54 note: expansion site 250 | :1:1: 3:58 note: in expansion of println! 251 | phrases/src/main.rs:4:5: 4:76 note: expansion site 252 | ``` 253 | 254 | `Rust`默认一切都是私有的。让我们深入了解一下这个。 255 | 256 | ### 导出公用接口 257 | 258 | `Rust`允许你严格的控制你的接口哪部分是公有的,所以它们默认都是私有的。你需要使用`pub`关键字,来公开它。让我们先关注`english`模块,所以让我们像这样减少`src/main.rs`的内容: 259 | 260 | ```rust 261 | extern crate phrases; 262 | 263 | fn main() { 264 | println!("Hello in English: {}", phrases::english::greetings::hello()); 265 | println!("Goodbye in English: {}", phrases::english::farewells::goodbye()); 266 | } 267 | ``` 268 | 269 | 在我们的`src/lib.rs`,让我们给`english`模块声明添加一个`pub`: 270 | 271 | ```rust 272 | pub mod english; 273 | mod japanese; 274 | ``` 275 | 276 | 然后在我们的`src/english/mod.rs`中,加上两个`pub`: 277 | 278 | ```rust 279 | pub mod greetings; 280 | pub mod farewells; 281 | ``` 282 | 283 | 在我们的`src/english/greetings.rs`中,让我们在`fn`声明中加上`pub`: 284 | 285 | ```rust 286 | pub fn hello() -> String { 287 | "Hello!".to_string() 288 | } 289 | ``` 290 | 291 | 然后在src/english/farewells.rs中: 292 | 293 | ```rust 294 | pub fn goodbye() -> String { 295 | "Goodbye.".to_string() 296 | } 297 | ``` 298 | 299 | 这样,我们的包装箱就可以编译了,虽然会有警告说我们没有使用`japanese`的方法: 300 | 301 | ```bash 302 | $ cargo run 303 | Compiling phrases v0.0.1 (file:///home/you/projects/phrases) 304 | src/japanese/greetings.rs:1:1: 3:2 warning: function is never used: `hello`, #[warn(dead_code)] on by default 305 | src/japanese/greetings.rs:1 fn hello() -> String { 306 | src/japanese/greetings.rs:2 "こんにちは".to_string() 307 | src/japanese/greetings.rs:3 } 308 | src/japanese/farewells.rs:1:1: 3:2 warning: function is never used: `goodbye`, #[warn(dead_code)] on by default 309 | src/japanese/farewells.rs:1 fn goodbye() -> String { 310 | src/japanese/farewells.rs:2 "さようなら".to_string() 311 | src/japanese/farewells.rs:3 } 312 | Running `target/debug/phrases` 313 | Hello in English: Hello! 314 | Goodbye in English: Goodbye. 315 | ``` 316 | 317 | 现在我们的函数是公有的了,我们可以使用它们。好的!然而,`phrases::english::greetings::hello()`非常长并且重复。`Rust` 有另一个关键字用来导入名字到当前空间中,这样我们就可以用更短的名字来引用它们。让我们聊聊`use`。 318 | 319 | ### 用`use`导入模块 320 | 321 | `Rust`有一个`use`关键字,它允许我们导入名字到我们本地的作用域中。让我们把`src/main.rs`改成这样: 322 | 323 | ```rust 324 | extern crate phrases; 325 | 326 | use phrases::english::greetings; 327 | use phrases::english::farewells; 328 | 329 | fn main() { 330 | println!("Hello in English: {}", greetings::hello()); 331 | println!("Goodbye in English: {}", farewells::goodbye()); 332 | } 333 | ``` 334 | 335 | 这两行`use`导入了两个模块到我们本地作用域中,这样我们就可以用一个短得多的名字来引用函数。作为一个传统,当导入函数时,导入模块而不是直接导入函数被认为是一个最佳实践。也就是说,你可以这么做: 336 | 337 | ```rust 338 | extern crate phrases; 339 | 340 | use phrases::english::greetings::hello; 341 | use phrases::english::farewells::goodbye; 342 | 343 | fn main() { 344 | println!("Hello in English: {}", hello()); 345 | println!("Goodbye in English: {}", goodbye()); 346 | } 347 | ``` 348 | 349 | 不过这并不理想。这意味着更加容易导致命名冲突。在我们的小程序中,这没什么大不了的,不过随着我们的程序增长,它将会成为一个问题。如果我们有命名冲突,`Rust`会给我们一个编译错误。举例来说,如果我们将`japanese的`函数设为公有,然后这样尝试: 350 | 351 | ```rust 352 | extern crate phrases; 353 | 354 | use phrases::english::greetings::hello; 355 | use phrases::japanese::greetings::hello; 356 | 357 | fn main() { 358 | println!("Hello in English: {}", hello()); 359 | println!("Hello in Japanese: {}", hello()); 360 | } 361 | ``` 362 | 363 | `Rust`会给我们一个编译时错误: 364 | 365 | ```bash 366 | Compiling phrases v0.0.1 (file:///home/you/projects/phrases) 367 | src/main.rs:4:5: 4:40 error: a value named `hello` has already been imported in this module [E0252] 368 | src/main.rs:4 use phrases::japanese::greetings::hello; 369 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 370 | error: aborting due to previous error 371 | Could not compile `phrases`. 372 | ``` 373 | 374 | 如果你从同样的模块中导入多个名字,我们不必写多遍。`Rust`有一个简便的语法: 375 | 376 | ```rust 377 | use phrases::english::greetings; 378 | use phrases::english::farewells; 379 | ``` 380 | 381 | 我们可以使用这个简写: 382 | 383 | ```rust 384 | use phrases::english::{greetings, farewells}; 385 | ``` 386 | 387 | ### 使用`pub use`重导出 388 | 389 | 你不仅可以用`use`来简化标识符。你也可以在包装箱内用它重导出函数到另一个模块中。这意味着你可以展示一个外部接口可能并不直接映射到内部代码结构。 390 | 391 | 让我们看个例子。修改`src/main.rs`让它看起来像这样: 392 | 393 | ```rust 394 | extern crate phrases; 395 | 396 | use phrases::english::{greetings,farewells}; 397 | use phrases::japanese; 398 | 399 | fn main() { 400 | println!("Hello in English: {}", greetings::hello()); 401 | println!("Goodbye in English: {}", farewells::goodbye()); 402 | 403 | println!("Hello in Japanese: {}", japanese::hello()); 404 | println!("Goodbye in Japanese: {}", japanese::goodbye()); 405 | } 406 | ``` 407 | 408 | 然后修改`src/lib.rs`公开`japanese`模块: 409 | 410 | ```rust 411 | pub mod english; 412 | pub mod japanese; 413 | ``` 414 | 415 | 接下来,把这两个函数声明为公有,先是`src/japanese/greetings.rs`: 416 | 417 | ```rust 418 | pub fn hello() -> String { 419 | "こんにちは".to_string() 420 | } 421 | ``` 422 | 423 | 然后是`src/japanese/farewells.rs`: 424 | 425 | ```rust 426 | pub fn goodbye() -> String { 427 | "さようなら".to_string() 428 | } 429 | ``` 430 | 431 | 最后,修改你的`src/japanese/mod.rs`为这样: 432 | 433 | ```rust 434 | pub use self::greetings::hello; 435 | pub use self::farewells::goodbye; 436 | 437 | mod greetings; 438 | mod farewells; 439 | ``` 440 | 441 | `pub use`声明将这些函数导入到了我们模块结构空间中。因为我们在japanese模块内使用了`pub use`,我们现在有了`phrases::japanese::hello()`和`phrases::japanese::goodbye()`函数,即使它们的代码在`phrases::japanese::greetings::hello()`和`phrases::japanese::farewells::goodbye()`函数中。内部结构并不反映外部接口。 442 | 443 | 这里我们对每个我们想导入到`japanese`空间的函数使用了`pub use`。我们也可以使用通配符来导入`greetings`的一切到当前空间中:`pub use self::greetings::*`。 444 | 445 | 那么`self`怎么办呢?好吧,默认,`use`声明是绝对路径,从你的包装箱根目录开始。`self`则使路径相对于你在结构中的当前位置。有一个更特殊的`use`形式:你可以使用`use super::`来到达你树中当前位置的上一级。一些同学喜欢把`self`看作`.`而把`super`看作`..`,它们在许`多shell`表示为当前目录和父目录。 446 | 447 | 除了`use`之外,路径是相对的:`foo::bar()`引用一个相对我们位置的`foo`中的函数。如果它带有`::`前缀,它引用了一个不同的`foo`,一个从你包装箱根开始的绝对路径。 448 | 449 | 另外,注意`pub use`出现在`mod`定义之前。`Rust`要求`use`位于最开始。 450 | 构建然后运行: 451 | 452 | ```bash 453 | $ cargo run 454 | Compiling phrases v0.0.1 (file:///home/you/projects/phrases) 455 | Running `target/debug/phrases` 456 | Hello in English: Hello! 457 | Goodbye in English: Goodbye. 458 | Hello in Japanese: こんにちは 459 | Goodbye in Japanese: さようなら 460 | ``` 461 | 462 | ### 复杂的导入 463 | 464 | `Rust`提供了多种高级选项来让你的`extern crate`和`use`语句变得简洁方便。这是一个例子: 465 | 466 | ```rust 467 | extern crate phrases as sayings; 468 | 469 | use sayings::japanese::greetings as ja_greetings; 470 | use sayings::japanese::farewells::*; 471 | use sayings::english::{self, greetings as en_greetings, farewells as en_farewells}; 472 | 473 | fn main() { 474 | println!("Hello in English; {}", en_greetings::hello()); 475 | println!("And in Japanese: {}", ja_greetings::hello()); 476 | println!("Goodbye in English: {}", english::farewells::goodbye()); 477 | println!("Again: {}", en_farewells::goodbye()); 478 | println!("And in Japanese: {}", goodbye()); 479 | } 480 | ``` 481 | 482 | 这里发生了什么? 483 | 484 | - 首先,`extern crate`和`use`都允许重命名导入的项。所以 `crate`仍然叫“`phrases`”,不过这里我们以“`sayings`”来引用它。类似的,第一个`use`语句从`crate`中导入`japanese::greetings`,不过作为`ja_greetings`而不是简单的`greetings`。这可以帮助我们消除来自不同包中相似名字的项的歧义。 485 | - 第二个use语句用了一个星号来引入`sayings::japanese::farewells`模块中的所有公有符号。如你所见之后我们可以不用模块标识来引用日语的`goodbye`函数。这类全局引用要保守使用。需要注意的是它只引入公有符号,哪怕在相同模块的代码中引入。 486 | - 第三个`use语`句需要更多的解释。它使用了“大括号扩展(`brace expansion`)”来将三条`use`语句压缩成了一条(这类语法对曾经写过`Linux shell`脚本的人应该很熟悉)。语句的非压缩形式应该是: 487 | 488 | ```rust 489 | use sayings::english; 490 | use sayings::english::greetings as en_greetings; 491 | use sayings::english::farewells as en_farewells; 492 | ``` 493 | 494 | 如你所见,大括号压缩了位于同一位置的多个项的`use`语句,而且在这里self指向这个位置。注意:大括号不能与星号嵌套或混合。 495 | 496 | ## 代码示例 497 | 498 | - [phrases](phrases) 499 | 500 | ## 扩展阅读 501 | 502 | > https://kaisery.gitbooks.io/rust-book-chinese/content/content/Crates%20and%20Modules%20crate%20和模块.html 503 | -------------------------------------------------------------------------------- /src/ch10/csv_challenge/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 3 | # Edit at https://www.gitignore.io/?templates=vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 4 | 5 | ### Eclipse ### 6 | .metadata 7 | bin/ 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .settings/ 15 | .loadpath 16 | .recommenders 17 | 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # PyDev specific (Python IDE for Eclipse) 25 | *.pydevproject 26 | 27 | # CDT-specific (C/C++ Development Tooling) 28 | .cproject 29 | 30 | # CDT- autotools 31 | .autotools 32 | 33 | # Java annotation processor (APT) 34 | .factorypath 35 | 36 | # PDT-specific (PHP Development Tools) 37 | .buildpath 38 | 39 | # sbteclipse plugin 40 | .target 41 | 42 | # Tern plugin 43 | .tern-project 44 | 45 | # TeXlipse plugin 46 | .texlipse 47 | 48 | # STS (Spring Tool Suite) 49 | .springBeans 50 | 51 | # Code Recommenders 52 | .recommenders/ 53 | 54 | # Annotation Processing 55 | .apt_generated/ 56 | 57 | # Scala IDE specific (Scala & Java development for Eclipse) 58 | .cache-main 59 | .scala_dependencies 60 | .worksheet 61 | 62 | ### Eclipse Patch ### 63 | # Eclipse Core 64 | .project 65 | 66 | # JDT-specific (Eclipse Java Development Tools) 67 | .classpath 68 | 69 | # Annotation Processing 70 | .apt_generated 71 | 72 | .sts4-cache/ 73 | 74 | ### GitBook ### 75 | # Node rules: 76 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 77 | .grunt 78 | 79 | ## Dependency directory 80 | ## Commenting this out is preferred by some people, see 81 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 82 | node_modules 83 | 84 | # Book build output 85 | _book 86 | 87 | # eBook build output 88 | *.epub 89 | *.mobi 90 | *.pdf 91 | 92 | ### Go ### 93 | # Binaries for programs and plugins 94 | *.exe 95 | *.exe~ 96 | *.dll 97 | *.so 98 | *.dylib 99 | 100 | # Test binary, built with `go test -c` 101 | *.test 102 | 103 | # Output of the go coverage tool, specifically when used with LiteIDE 104 | *.out 105 | 106 | # Dependency directories (remove the comment below to include it) 107 | # vendor/ 108 | 109 | ### Go Patch ### 110 | /vendor/ 111 | /Godeps/ 112 | 113 | ### Java ### 114 | # Compiled class file 115 | *.class 116 | 117 | # Log file 118 | *.log 119 | 120 | # BlueJ files 121 | *.ctxt 122 | 123 | # Mobile Tools for Java (J2ME) 124 | .mtj.tmp/ 125 | 126 | # Package Files # 127 | *.jar 128 | *.war 129 | *.nar 130 | *.ear 131 | *.zip 132 | *.tar.gz 133 | *.rar 134 | 135 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 136 | hs_err_pid* 137 | 138 | ### JetBrains+all ### 139 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 140 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 141 | 142 | # User-specific stuff 143 | .idea/**/workspace.xml 144 | .idea/**/tasks.xml 145 | .idea/**/usage.statistics.xml 146 | .idea/**/dictionaries 147 | .idea/**/shelf 148 | 149 | # Generated files 150 | .idea/**/contentModel.xml 151 | 152 | # Sensitive or high-churn files 153 | .idea/**/dataSources/ 154 | .idea/**/dataSources.ids 155 | .idea/**/dataSources.local.xml 156 | .idea/**/sqlDataSources.xml 157 | .idea/**/dynamic.xml 158 | .idea/**/uiDesigner.xml 159 | .idea/**/dbnavigator.xml 160 | 161 | # Gradle 162 | .idea/**/gradle.xml 163 | .idea/**/libraries 164 | 165 | # Gradle and Maven with auto-import 166 | # When using Gradle or Maven with auto-import, you should exclude module files, 167 | # since they will be recreated, and may cause churn. Uncomment if using 168 | # auto-import. 169 | # .idea/modules.xml 170 | # .idea/*.iml 171 | # .idea/modules 172 | # *.iml 173 | # *.ipr 174 | 175 | # CMake 176 | cmake-build-*/ 177 | 178 | # Mongo Explorer plugin 179 | .idea/**/mongoSettings.xml 180 | 181 | # File-based project format 182 | *.iws 183 | 184 | # IntelliJ 185 | out/ 186 | 187 | # mpeltonen/sbt-idea plugin 188 | .idea_modules/ 189 | 190 | # JIRA plugin 191 | atlassian-ide-plugin.xml 192 | 193 | # Cursive Clojure plugin 194 | .idea/replstate.xml 195 | 196 | # Crashlytics plugin (for Android Studio and IntelliJ) 197 | com_crashlytics_export_strings.xml 198 | crashlytics.properties 199 | crashlytics-build.properties 200 | fabric.properties 201 | 202 | # Editor-based Rest Client 203 | .idea/httpRequests 204 | 205 | # Android studio 3.1+ serialized cache file 206 | .idea/caches/build_file_checksums.ser 207 | 208 | ### JetBrains+all Patch ### 209 | # Ignores the whole .idea folder and all .iml files 210 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 211 | 212 | .idea/ 213 | 214 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 215 | 216 | *.iml 217 | modules.xml 218 | .idea/misc.xml 219 | *.ipr 220 | 221 | # Sonarlint plugin 222 | .idea/sonarlint 223 | 224 | ### Linux ### 225 | *~ 226 | 227 | # temporary files which can be created if a process still has a handle open of a deleted file 228 | .fuse_hidden* 229 | 230 | # KDE directory preferences 231 | .directory 232 | 233 | # Linux trash folder which might appear on any partition or disk 234 | .Trash-* 235 | 236 | # .nfs files are created when an open file is removed but is still being accessed 237 | .nfs* 238 | 239 | ### macOS ### 240 | # General 241 | .DS_Store 242 | .AppleDouble 243 | .LSOverride 244 | 245 | # Icon must end with two \r 246 | Icon 247 | 248 | # Thumbnails 249 | ._* 250 | 251 | # Files that might appear in the root of a volume 252 | .DocumentRevisions-V100 253 | .fseventsd 254 | .Spotlight-V100 255 | .TemporaryItems 256 | .Trashes 257 | .VolumeIcon.icns 258 | .com.apple.timemachine.donotpresent 259 | 260 | # Directories potentially created on remote AFP share 261 | .AppleDB 262 | .AppleDesktop 263 | Network Trash Folder 264 | Temporary Items 265 | .apdisk 266 | 267 | ### Maven ### 268 | target/ 269 | pom.xml.tag 270 | pom.xml.releaseBackup 271 | pom.xml.versionsBackup 272 | pom.xml.next 273 | release.properties 274 | dependency-reduced-pom.xml 275 | buildNumber.properties 276 | .mvn/timing.properties 277 | .mvn/wrapper/maven-wrapper.jar 278 | .flattened-pom.xml 279 | 280 | ### OSX ### 281 | # General 282 | 283 | # Icon must end with two \r 284 | 285 | # Thumbnails 286 | 287 | # Files that might appear in the root of a volume 288 | 289 | # Directories potentially created on remote AFP share 290 | 291 | ### Rust ### 292 | # Generated by Cargo 293 | # will have compiled files and executables 294 | /target/ 295 | 296 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 297 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 298 | Cargo.lock 299 | 300 | # These are backup files generated by rustfmt 301 | **/*.rs.bk 302 | 303 | ### Vim ### 304 | # Swap 305 | [._]*.s[a-v][a-z] 306 | [._]*.sw[a-p] 307 | [._]s[a-rt-v][a-z] 308 | [._]ss[a-gi-z] 309 | [._]sw[a-p] 310 | 311 | # Session 312 | Session.vim 313 | Sessionx.vim 314 | 315 | # Temporary 316 | .netrwhist 317 | # Auto-generated tag files 318 | tags 319 | # Persistent undo 320 | [._]*.un~ 321 | 322 | ### VisualStudioCode ### 323 | .vscode/* 324 | !.vscode/settings.json 325 | !.vscode/tasks.json 326 | !.vscode/launch.json 327 | !.vscode/extensions.json 328 | 329 | ### VisualStudioCode Patch ### 330 | # Ignore all local history of files 331 | .history 332 | 333 | ### Windows ### 334 | # Windows thumbnail cache files 335 | Thumbs.db 336 | Thumbs.db:encryptable 337 | ehthumbs.db 338 | ehthumbs_vista.db 339 | 340 | # Dump file 341 | *.stackdump 342 | 343 | # Folder config file 344 | [Dd]esktop.ini 345 | 346 | # Recycle Bin used on file shares 347 | $RECYCLE.BIN/ 348 | 349 | # Windows Installer files 350 | *.cab 351 | *.msi 352 | *.msix 353 | *.msm 354 | *.msp 355 | 356 | # Windows shortcuts 357 | *.lnk 358 | 359 | # End of https://www.gitignore.io/api/vim,osx,macos,linux,windows,visualstudiocode,jetbrains+all,eclipse,java,maven,go,rust,gitbook 360 | -------------------------------------------------------------------------------- /src/ch10/csv_challenge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "csv_challenge" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | structopt = "0.2" 11 | structopt-derive = "0.2" 12 | -------------------------------------------------------------------------------- /src/ch10/csv_challenge/README.md: -------------------------------------------------------------------------------- 1 | # CVS Challenge 2 | 3 | - [CVS Challenge](#cvs-challenge) 4 | - [项目介绍](#%e9%a1%b9%e7%9b%ae%e4%bb%8b%e7%bb%8d) 5 | - [步骤](#%e6%ad%a5%e9%aa%a4) 6 | - [1. 创建项目](#1-%e5%88%9b%e5%bb%ba%e9%a1%b9%e7%9b%ae) 7 | - [2. 使用`structopt`解析命令行参数](#2-%e4%bd%bf%e7%94%a8structopt%e8%a7%a3%e6%9e%90%e5%91%bd%e4%bb%a4%e8%a1%8c%e5%8f%82%e6%95%b0) 8 | - [3. 添加`opt`模块](#3-%e6%b7%bb%e5%8a%a0opt%e6%a8%a1%e5%9d%97) 9 | - [4. 添加对`csv`的核心处理模块](#4-%e6%b7%bb%e5%8a%a0%e5%af%b9csv%e7%9a%84%e6%a0%b8%e5%bf%83%e5%a4%84%e7%90%86%e6%a8%a1%e5%9d%97) 10 | - [5. 添加单元测试](#5-%e6%b7%bb%e5%8a%a0%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95) 11 | - [6. 添加集成测试](#6-%e6%b7%bb%e5%8a%a0%e9%9b%86%e6%88%90%e6%b5%8b%e8%af%95) 12 | - [6.1. 新增`src/lib.rs`,将所有模块引入其中,并暴露对外可以调用的函数。](#61-%e6%96%b0%e5%a2%9esrclibrs%e5%b0%86%e6%89%80%e6%9c%89%e6%a8%a1%e5%9d%97%e5%bc%95%e5%85%a5%e5%85%b6%e4%b8%ad%e5%b9%b6%e6%9a%b4%e9%9c%b2%e5%af%b9%e5%a4%96%e5%8f%af%e4%bb%a5%e8%b0%83%e7%94%a8%e7%9a%84%e5%87%bd%e6%95%b0) 13 | - [6.2. 修改`main.rs`](#62-%e4%bf%ae%e6%94%b9mainrs) 14 | - [6.3. 创建`tests/integration_test.rs`文件](#63-%e5%88%9b%e5%bb%batestsintegrationtestrs%e6%96%87%e4%bb%b6) 15 | - [7. 添加性能基准测试](#7-%e6%b7%bb%e5%8a%a0%e6%80%a7%e8%83%bd%e5%9f%ba%e5%87%86%e6%b5%8b%e8%af%95) 16 | - [7.1. 创建`benches/file_op_bench.rs`](#71-%e5%88%9b%e5%bb%babenchesfileopbenchrs) 17 | - [7.2. 安装并使用`rust nightly`版本进行基准测试](#72-%e5%ae%89%e8%a3%85%e5%b9%b6%e4%bd%bf%e7%94%a8rust-nightly%e7%89%88%e6%9c%ac%e8%bf%9b%e8%a1%8c%e5%9f%ba%e5%87%86%e6%b5%8b%e8%af%95) 18 | 19 | ## 项目介绍 20 | 21 | 本项目主要是用来验证`Rust`模块化编程。 22 | 23 | ## 步骤 24 | 25 | ### 1. 创建项目 26 | 27 | ```bash 28 | cargo new --bin csv_challenge 29 | ``` 30 | 31 | > 创建二进制项目。`cargo`默认使用了`--bin`参数。 32 | 33 | 默认目录结构 34 | 35 | ```bash 36 | . 37 | ├── Cargo.toml 38 | ├── src 39 | │ └── main.rs 40 | ``` 41 | 42 | ### 2. 使用`structopt`解析命令行参数 43 | 44 | `structopt`是基于`clap`的基础上构建而成,简化了操作。 45 | 46 | 添加依赖 47 | 48 | ```toml 49 | [dependencies] 50 | structopt = "0.2" 51 | structopt-derive = "0.2" 52 | ``` 53 | 54 | > 因为`structopt`是基于过程宏(`Procedural Macro`)的,所以它依赖`sructopt-derive`包。 55 | 56 | ### 3. 添加`opt`模块 57 | 58 | 创建`src/opt.rs` 59 | 60 | ```rust 61 | use structopt_derive::*; 62 | #[derive(StructOpt, Debug)] 63 | #[structopt(name = "csv_challenge", about = "Usage")] 64 | pub struct Opt { 65 | #[structopt(help = "Input file")] 66 | pub input : String, 67 | #[structopt(help = "Column Name")] 68 | pub column_name: String, 69 | #[structopt(help = "Replcaement Column Name")] 70 | pub replacement:String, 71 | #[structopt(help ="Output file, stdout if not present")] 72 | pub output: Option, 73 | } 74 | ``` 75 | 76 | 在`main.rs`中来引用`Opt`。 77 | 78 | ```rust 79 | mod opt; 80 | use self::opt::Opt; 81 | 82 | fn main(){ 83 | let opt = Opt::from_args(); 84 | } 85 | ``` 86 | 87 | ### 4. 添加对`csv`的核心处理模块 88 | 89 | 添加`src/core/read.rs`和`src/core/write.rs`两个文件 90 | 91 | - read.rs 92 | 93 | ```rust 94 | use super::{Error, PathBuf,File,Read,Write}; 95 | 96 | pub fn load_csv(csv_file: PathBuf) -> Result { 97 | let file = read(csv_file)?; 98 | Ok(file) 99 | } 100 | 101 | pub fn write_csv(csv_data: &str, filename: &str) -> Result<(),Error> { 102 | write(csv_data,filename)?; 103 | Ok(()) 104 | } 105 | 106 | fn read(path: PathBuf) -> Result { 107 | let mut buffer = String::new(); 108 | let mut file = open(path)?; 109 | file.read_to_string(&mut buffer)?; 110 | 111 | if buffer.is_empty() { 112 | return Err("input file missing")? 113 | } 114 | 115 | Ok(buffer) 116 | } 117 | 118 | fn open(path: PathBuf) -> Result { 119 | let file = File::open(path)?; 120 | Ok(file) 121 | } 122 | 123 | fn write(data: &str, filename : &str) -> Result<(), Error> { 124 | let mut buffer = File::create(filename)?; 125 | buffer.write_all(data.as_bytes())?; 126 | Ok(()) 127 | } 128 | ``` 129 | 130 | - write.rs 131 | 132 | ```rust 133 | use super::*; 134 | 135 | pub fn replace_column(data: String, column: &str, replacement: &str) -> Result { 136 | let mut lines = data.lines(); 137 | let headers = lines.next().unwrap(); 138 | 139 | let columns : Vec<&str> = headers.split(',').collect(); 140 | 141 | let column_number = columns.iter().position(|&e| e == column); 142 | let column_number = match column_number { 143 | Some(column) => column, 144 | None => Err("column name doesn't exist in the input file")?, 145 | }; 146 | 147 | let mut result = String::with_capacity(data.capacity()); 148 | 149 | result.push_str(&columns.join(",")); 150 | result.push('\n'); 151 | 152 | for line in lines { 153 | let mut records : Vec<&str> = line.split(',').collect(); 154 | records[column_number] = replacement; 155 | result.push_str(&records.join(",")); 156 | result.push('\n'); 157 | } 158 | 159 | Ok(result) 160 | } 161 | ``` 162 | 163 | ### 5. 添加单元测试 164 | 165 | ```rust 166 | #[cfg(test)] 167 | mod test { 168 | use std::path::PathBuf; 169 | use super::load_csv; 170 | 171 | #[test] 172 | fn test_valid_load_csv() { 173 | let filename = PathBuf::from("./input/challenge.csv"); 174 | let csv_data =load_csv(filename); 175 | assert!(csv_data.is_ok()); 176 | } 177 | } 178 | ``` 179 | 180 | ### 6. 添加集成测试 181 | 182 | `Rust`对于二进制是不能添加集成测试的,因为二进制包只能独立使用,并不能对外提供可调用的函数,需要进行改造。 183 | 184 | #### 6.1. 新增`src/lib.rs`,将所有模块引入其中,并暴露对外可以调用的函数。 185 | 186 | ```rust 187 | mod opt; 188 | mod err; 189 | mod core; 190 | 191 | pub use self::opt::Opt; 192 | pub use self::core::{ 193 | read::{load_csv,write_csv}, 194 | write::replace_column, 195 | }; 196 | ``` 197 | 198 | #### 6.2. 修改`main.rs` 199 | 200 | ```rust 201 | use csv_challenge::{ 202 | Opt, 203 | {load_csv,write_csv}, 204 | replace_column, 205 | }; 206 | ``` 207 | 208 | > 这种`main.rs`配合`lib.rs`的形式,是二进制包的**最佳实践** 209 | 210 | #### 6.3. 创建`tests/integration_test.rs`文件 211 | 212 | ```rust 213 | #[cfg(test)] 214 | 215 | mod test{ 216 | use std::path::PathBuf; 217 | use std::fs; 218 | 219 | use csv_challenge::{ 220 | {load_csv,write_csv}, 221 | replace_column, 222 | }; 223 | 224 | #[test] 225 | fn test_csv_challenge() { 226 | let filename = PathBuf::from("./input/challenge.csv"); 227 | let csv_data = load_csv(filename).unwrap(); 228 | let modified_data = replace_column(csv_data,"City","Beijing").unwrap(); 229 | let output_file = write_csv(&modified_data, "output/test.csv"); 230 | 231 | assert!(output_file.is_ok()); 232 | 233 | fs::remove_file("output/test.csv"); 234 | } 235 | } 236 | ``` 237 | 238 | ### 7. 添加性能基准测试 239 | 240 | #### 7.1. 创建`benches/file_op_bench.rs` 241 | 242 | ```rust 243 | #![feature(test)] 244 | extern crate test; 245 | use test::Bencher; 246 | use std::path::PathBuf; 247 | use csv_challenge::{ 248 | Opt, 249 | {load_csv, write_csv}, 250 | replace_column, 251 | }; 252 | 253 | #[bench] 254 | fn bench_read_100times(b: &mut test::Bencher) { 255 | b.iter(|| { 256 | let n = test::black_box(100); 257 | (0..n).fold(0, |_,_|{test_load_csv();0}) 258 | }); 259 | } 260 | 261 | fn test_load_csv() { 262 | let filename = PathBuf::from("./input/challenge.csv"); 263 | load_csv(filename); 264 | } 265 | ``` 266 | 267 | > 注意:要使用基准测试,必须启用`#![feature(test)]`。但是只能在`rust nightly`版本中使用。 268 | 269 | #### 7.2. 安装并使用`rust nightly`版本进行基准测试 270 | 271 | - 安装`nightly`版本 272 | 273 | ```bash 274 | rustup toolchian install nightly 275 | rustup default nightly 276 | ``` 277 | 278 | - 运行基准测试 279 | 280 | ```bash 281 | cargo bench 282 | ``` 283 | 284 | 基准测试运行结果 285 | 286 | ```bash 287 | Running target/release/deps/file_op_bench-4a13631faeeecf0a 288 | 289 | running 1 test 290 | test bench_read_100times ... bench: 2,019,512 ns/iter (+/- 1,480,889) 291 | 292 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out 293 | ``` -------------------------------------------------------------------------------- /src/ch10/csv_challenge/benches/file_op_bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | use test::Bencher; 4 | use std::path::PathBuf; 5 | use csv_challenge::{ 6 | Opt, 7 | {load_csv, write_csv}, 8 | replace_column, 9 | }; 10 | 11 | #[bench] 12 | fn bench_read_100times(b: &mut test::Bencher) { 13 | b.iter(|| { 14 | let n = test::black_box(100); 15 | (0..n).fold(0, |_,_|{test_load_csv();0}) 16 | }); 17 | } 18 | 19 | fn test_load_csv() { 20 | let filename = PathBuf::from("./input/challenge.csv"); 21 | load_csv(filename); 22 | } -------------------------------------------------------------------------------- /src/ch10/csv_challenge/input/challenge.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Age,City,Eyes color,Species 2 | John,Dow,32,Tokyo,Blue,Human 3 | Flip,Heln,12,Canberra,Red,Unknown 4 | Terdos,Bendarian,165,Cracow,Blue,Magic tree 5 | Dominik,Elpos,33,Paris,Purple,Orc 6 | Brad,Doe,42,Dublin,Blue,Human -------------------------------------------------------------------------------- /src/ch10/csv_challenge/output/output.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Age,City,Eyes color,Species 2 | John,Dow,32,Beijing,Blue,Human 3 | Flip,Heln,12,Beijing,Red,Unknown 4 | Terdos,Bendarian,165,Beijing,Blue,Magic tree 5 | Dominik,Elpos,33,Beijing,Purple,Orc 6 | Brad,Doe,42,Beijing,Blue,Human 7 | -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/core.rs: -------------------------------------------------------------------------------- 1 | // core.rs 2 | pub mod read; 3 | pub mod write; 4 | 5 | use crate::err::Error; 6 | 7 | /** 8 | * 考虑处理替换同样需要PathBuf,File和Error这三种类型的支持,为了避免代码重复,现在将引入这三种类型的代码移入到core.rs 9 | */ 10 | use std::{ 11 | path::PathBuf, 12 | fs::File, 13 | io::{Read,Write} 14 | }; -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/core/read.rs: -------------------------------------------------------------------------------- 1 | /** 2 | * 作用于core/read.rs 3 | * 将在read.rs中引入的std模块都溢出来,使用了Rust 2018新模块系统支持的use语句内嵌语法 4 | * */ 5 | use super::{Error, PathBuf,File,Read,Write}; 6 | 7 | pub fn load_csv(csv_file: PathBuf) -> Result { 8 | let file = read(csv_file)?; 9 | Ok(file) 10 | } 11 | /// # Usage: 12 | /// 13 | /// ```ignore 14 | /// let filename = PathBuf::from("./files/challenge.csv"); 15 | /// let csv_data = load_csv(filename).unwrap(); 16 | /// let modified_data = replace_column( 17 | /// csv_data, "City","Beijing".unwap(); 18 | /// ) 19 | /// let output_file = write_csv(&modified_data, "output/test.csv"); 20 | /// assert!(output_file(output_file.is_os())); 21 | /// ``` 22 | pub fn write_csv(csv_data: &str, filename: &str) -> Result<(),Error> { 23 | write(csv_data,filename)?; 24 | Ok(()) 25 | } 26 | 27 | fn read(path: PathBuf) -> Result { 28 | let mut buffer = String::new(); 29 | let mut file = open(path)?; 30 | file.read_to_string(&mut buffer)?; 31 | 32 | if buffer.is_empty() { 33 | return Err("input file missing")? 34 | } 35 | 36 | Ok(buffer) 37 | } 38 | 39 | fn open(path: PathBuf) -> Result { 40 | let file = File::open(path)?; 41 | Ok(file) 42 | } 43 | 44 | fn write(data: &str, filename : &str) -> Result<(), Error> { 45 | let mut buffer = File::create(filename)?; 46 | buffer.write_all(data.as_bytes())?; 47 | Ok(()) 48 | } 49 | 50 | #[cfg(test)] 51 | mod test{ 52 | use std::path::PathBuf; 53 | use super::{load_csv,write_csv}; 54 | 55 | #[test] 56 | fn test_valid_load_csv() { 57 | let filename = PathBuf::from("./input/challenge.csv"); 58 | let csv_data = load_csv(filename); 59 | assert!(csv_data.is_ok()); 60 | } 61 | 62 | #[test] 63 | #[ignore] 64 | fn test_invaild_load_csv() { 65 | let filename =PathBuf::from("./input/other.csv"); 66 | let csv_data = load_csv(filename); 67 | assert!(csv_data.is_ok()); 68 | } 69 | 70 | #[test] 71 | fn test_valid_write_csv(){ 72 | let filename = PathBuf::from("./input/challenge.csv"); 73 | let modified_data = r"a,b,c,d,e\nf,g,h,i,j"; 74 | let output_file = write_csv(&modified_data, "output/test.csv"); 75 | assert!(output_file.is_ok()); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/core/write.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub fn replace_column(data: String, column: &str, replacement: &str) -> Result { 4 | let mut lines = data.lines(); 5 | let headers = lines.next().unwrap(); 6 | 7 | let columns : Vec<&str> = headers.split(',').collect(); 8 | 9 | let column_number = columns.iter().position(|&e| e == column); 10 | let column_number = match column_number { 11 | Some(column) => column, 12 | None => Err("column name doesn't exist in the input file")?, 13 | }; 14 | 15 | let mut result = String::with_capacity(data.capacity()); 16 | 17 | result.push_str(&columns.join(",")); 18 | result.push('\n'); 19 | 20 | for line in lines { 21 | let mut records : Vec<&str> = line.split(',').collect(); 22 | records[column_number] = replacement; 23 | result.push_str(&records.join(",")); 24 | result.push('\n'); 25 | } 26 | 27 | Ok(result) 28 | } -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/err.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | #[derive(Debug)] 4 | pub enum Error { 5 | Io(io::Error), //I/O错误 6 | Program(&'static str), //程序错误 7 | } 8 | 9 | impl From for Error { 10 | fn from(e: io::Error) -> Error { 11 | Error::Io(e) 12 | } 13 | } 14 | 15 | impl From<&'static str> for Error { 16 | fn from(e: &'static str) -> Error{ 17 | Error::Program(e) 18 | } 19 | } -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is documentation for the `csv_challenge` lib crate 2 | //! 3 | //! Usage: 4 | //! 5 | //! ``` 6 | //! use csv_challenge::{ 7 | //! Opt, 8 | //! {load_csv,write_csv}, 9 | //! replace_column, 10 | //! }; 11 | //! ``` 12 | mod opt; 13 | mod err; 14 | mod core; 15 | 16 | /*** 17 | * 重新导出(Re-exporting) 18 | * 简化外包调用的导出路径,而且也不需要对外暴露模块 19 | */ 20 | pub use self::opt::Opt; 21 | pub use self::core::{ 22 | read::{load_csv,write_csv}, 23 | write::replace_column, 24 | }; -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/main.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | use csv_challenge::{ 4 | Opt, 5 | {load_csv,write_csv}, 6 | replace_column, 7 | }; 8 | // mod opt; 9 | // use self::opt::Opt; 10 | // mod err; 11 | // mod core; 12 | // use self::core::{ 13 | // read::{load_csv,write_csv}, 14 | // write::replace_column, 15 | // }; 16 | 17 | use std::process; 18 | use std::path::PathBuf; 19 | 20 | fn main() { 21 | let opt = Opt::from_args(); 22 | // 将opt.input字段中输入的CSV文件路径字符串转换成PathBuf类型 23 | let filename = PathBuf::from(opt.input); 24 | 25 | // 将得到的CSV文件路径filename传入load_csv函数中,使用match来处理load_csv返回的Result类型 26 | let csv_data = match load_csv(filename) { 27 | Ok(fname) => {fname}, 28 | Err(e) => { 29 | println!("main error: {:?}",e); 30 | process::exit(1); 31 | }, 32 | }; 33 | 34 | // 使用replace_column方法替换原始CSV内容并生成新的修改过的内容modified_data 35 | let modified_data = match replace_column(csv_data, &opt.column_name, &opt.replacement) { 36 | Ok(data) => data, 37 | Err(e) => { 38 | println!("main error: {:?}",e); 39 | process::exit(1); 40 | }, 41 | }; 42 | 43 | //声明output变量绑定,代表输出csv文件路径,因为opt.output是可以忽略的参数,所以使用unwrap_or方法来定义默认输出路径 44 | let output_file = &opt.output.unwrap_or("output/output.csv".to_string()); 45 | 46 | // 将督导的原始CSV文件内容csv_data和输出路径output_file传入write_csv方法中来输出CSV文件 47 | match write_csv(&modified_data, &output_file) { 48 | Ok(_) => { 49 | println!("write success!"); 50 | }, 51 | Err(e) => { 52 | println!("main error: {:?}",e); 53 | process::exit(1); 54 | }, 55 | } 56 | // println!("{:?}",opt); 57 | } 58 | 59 | 60 | #[cfg(test)] 61 | mod test { 62 | use std::path::PathBuf; 63 | use super::load_csv; 64 | 65 | #[test] 66 | fn test_valid_load_csv() { 67 | let filename = PathBuf::from("./input/challenge.csv"); 68 | let csv_data =load_csv(filename); 69 | assert!(csv_data.is_ok()); 70 | } 71 | } -------------------------------------------------------------------------------- /src/ch10/csv_challenge/src/opt.rs: -------------------------------------------------------------------------------- 1 | use structopt_derive::*; 2 | #[derive(StructOpt, Debug)] 3 | #[structopt(name = "csv_challenge", about = "Usage")] 4 | pub struct Opt { 5 | #[structopt(help = "Input file")] 6 | pub input : String, 7 | #[structopt(help = "Column Name")] 8 | pub column_name: String, 9 | #[structopt(help = "Replcaement Column Name")] 10 | pub replacement:String, 11 | #[structopt(help ="Output file, stdout if not present")] 12 | pub output: Option, // 使用Option表示可以忽略的参数 13 | } -------------------------------------------------------------------------------- /src/ch10/csv_challenge/tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | 3 | mod test{ 4 | use std::path::PathBuf; 5 | use std::fs; 6 | 7 | use csv_challenge::{ 8 | {load_csv,write_csv}, 9 | replace_column, 10 | }; 11 | 12 | #[test] 13 | fn test_csv_challenge() { 14 | let filename = PathBuf::from("./input/challenge.csv"); 15 | let csv_data = load_csv(filename).unwrap(); 16 | let modified_data = replace_column(csv_data,"City","Beijing").unwrap(); 17 | let output_file = write_csv(&modified_data, "output/test.csv"); 18 | 19 | assert!(output_file.is_ok()); 20 | 21 | fs::remove_file("output/test.csv"); 22 | } 23 | } -------------------------------------------------------------------------------- /src/ch10/mod.rs: -------------------------------------------------------------------------------- 1 | //! 模块化开发 2 | //! 3 | 4 | pub fn title() { 5 | println!("第10章: {}","模块化开发"); 6 | } -------------------------------------------------------------------------------- /src/ch10/phrases/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phrases" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /src/ch10/phrases/src/chinese.rs: -------------------------------------------------------------------------------- 1 | mod farewells; 2 | mod greetings; -------------------------------------------------------------------------------- /src/ch10/phrases/src/chinese/farewells.rs: -------------------------------------------------------------------------------- 1 | fn goodbye() -> String { 2 | "再见!".to_string() 3 | } -------------------------------------------------------------------------------- /src/ch10/phrases/src/chinese/greetings.rs: -------------------------------------------------------------------------------- 1 | fn hello() -> String{ 2 | "你好!".to_string() 3 | } -------------------------------------------------------------------------------- /src/ch10/phrases/src/english/farewells.rs: -------------------------------------------------------------------------------- 1 | pub fn goodbye() -> String { 2 | "Goodbye!".to_string() 3 | } -------------------------------------------------------------------------------- /src/ch10/phrases/src/english/greetings.rs: -------------------------------------------------------------------------------- 1 | pub fn hello() -> String{ 2 | "Hello!".to_string() 3 | } -------------------------------------------------------------------------------- /src/ch10/phrases/src/english/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod farewells; 2 | pub mod greetings; -------------------------------------------------------------------------------- /src/ch10/phrases/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod english; 2 | mod chinese; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | #[test] 7 | fn it_works() { 8 | assert_eq!(2 + 2, 4); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/ch10/phrases/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate phrases; 2 | 3 | use phrases::english::{greetings, farewells}; 4 | 5 | fn main() { 6 | println!("Hello in English : {}", greetings::hello()); 7 | println!("Goodbye in English : {}", farewells::goodbye()); 8 | } -------------------------------------------------------------------------------- /src/ch10/static_hashmap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "static_hashmap" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | regex = "1.3.1" 11 | lazy_static = "1.4.0" -------------------------------------------------------------------------------- /src/ch10/static_hashmap/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | #[test] 4 | fn it_works() { 5 | assert_eq!(2 + 2, 4); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ch10/static_hashmap/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | extern crate regex; 5 | 6 | mod static_kv { 7 | use std::collections::HashMap; 8 | use std::sync::RwLock; 9 | 10 | // 定义了公开的普通常量NF,它是一个脂肪层字面量类型 11 | // 如果需要使用,就必须带上命名空间, static_kv::NF 12 | pub const NF: &'static str = "not found"; 13 | 14 | // 使用lazy_static!宏定义了两个全局静态变量MAP和MAP_MUT,分别代表只读的HashMap和可变的HashMap。 15 | /*** 16 | * lazy_static!宏语法格式 17 | * 18 | * lazy_static! { 19 | * 20 | * [pub static ref NAME_1 : TYPE_1 = EXPR_1; 21 | * [pub] static ref NAME_1 : TYPE_1 = EXPR_1; 22 | * } 23 | * 24 | * 必须严格按照此语法格式来书写,否则会引发线程恐慌 25 | */ 26 | lazy_static! { 27 | pub static ref MAP: HashMap = { 28 | let mut m = HashMap::new(); 29 | m.insert(0, "foo"); 30 | m 31 | }; 32 | 33 | pub static ref MAP_MUT : RwLock> = { 34 | let mut m = HashMap::new(); 35 | m.insert(0, "bar"); 36 | RwLock::new(m) 37 | }; 38 | } 39 | } 40 | 41 | fn read_kv() { 42 | let ref m = static_kv::MAP; 43 | assert_eq!("foo", *m.get(&0).unwrap_or(&static_kv::NF)); 44 | 45 | assert_eq!(static_kv::NF, *m.get(&1).unwrap_or(&static_kv::NF)); 46 | } 47 | 48 | fn rw_mut_kv() -> Result<(),String> { 49 | { 50 | let m = static_kv::MAP_MUT.read().map_err(|e| e.to_string())?; 51 | 52 | assert_eq!("bar", *m.get(&0).unwrap_or(&static_kv::NF)); 53 | } 54 | { 55 | let mut m = static_kv::MAP_MUT.write().map_err(|e| e.to_string())?; 56 | m.insert(1, "baz"); 57 | } 58 | Ok(()) 59 | } 60 | 61 | fn main() { 62 | println!("static hashmap"); 63 | read_kv(); 64 | 65 | match rw_mut_kv() { 66 | Ok(()) => { 67 | let m = static_kv::MAP_MUT.read().map_err(|e| e.to_string()).unwrap(); 68 | assert_eq!("baz", *m.get(&1).unwrap_or(&static_kv::NF)); 69 | }, 70 | Err(e) => {println!("Error {}",e)}, 71 | } 72 | } -------------------------------------------------------------------------------- /src/ch10/use_regex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "use_regex" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | # regex包支持大部分正则匹配功能,但不支持环视(look-around)和反引用(backreference)。 11 | # 如果一定要使用环视和反向引用,可以使用fancy-regex包 12 | regex = "1.0.5" 13 | -------------------------------------------------------------------------------- /src/ch10/use_regex/README.md: -------------------------------------------------------------------------------- 1 | # use_regex 2 | 3 | `regex`包支持多种正则表达式标记,意义如下: 4 | 5 | - `i`,匹配时不区分大小写 6 | - `m`,多行模式,`"^"`和`"$"`对应行首和行尾 7 | - `s`,允许通配符`"."`匹配`"\n"` 8 | - `U`,大写`U`,交换`"x*"`和`"x*?"`的意义 9 | - `u`,允许支持`Unicode`(默认启动) 10 | - `x`,忽略空格并允许允许行注释(以`#`开头) 11 | -------------------------------------------------------------------------------- /src/ch10/use_regex/src/main.rs: -------------------------------------------------------------------------------- 1 | // 使用exter crate regex声明引入regex包 2 | extern crate regex; 3 | //使用use来声明regex::Regex,要不然就要在代码中直接使用regex::Regex::new,可读性就差了许多 4 | use regex::Regex; 5 | 6 | const TO_SEATCH: &'static str = " 7 | On 2017-12-31, happy. On 2018-01-01, New Year. 8 | "; 9 | 10 | fn main() { 11 | // 带有捕获组的表达式 12 | let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); 13 | //使用captures_iter对给定的TO)_SEATCH进行匹配和迭代,并依次捕获匹配到的字符串答应出来。 14 | for caps in re.captures_iter(TO_SEATCH) { 15 | println!("year: {}, month: {}, day {}", 16 | caps.get(1).unwrap().as_str(), 17 | caps.get(2).unwrap().as_str(), 18 | caps.get(3).unwrap().as_str()); 19 | } 20 | } 21 | 22 | 23 | #[cfg(test)] 24 | 25 | mod test{ 26 | use regex::Regex; 27 | 28 | /** 29 | * 测试使用命名捕获的示例 30 | */ 31 | #[test] 32 | fn test_named_capture() { 33 | 34 | // 在正则表达式中加上空格和注释也不影响最终匹配的结果 35 | // 正则表达式以(?x)为前缀,这是指定了正则表达式标记x 36 | // 在表达式汇总使用(?Pexp)这种格式来定义命名捕获组 37 | let re = Regex::new(r"(?x) 38 | (?P\d{4}) # the year 39 | - 40 | (?P\d{2}) # the month 41 | - 42 | (?P\d{2}) # the day 43 | ").unwrap(); 44 | // 使用captures方法获取匹配的捕获变量,并保存到一个HashMap中。以命名变量作为HashMap的Key,匹配的字符串作为值。 45 | let caps = re.captures("2019-01-01").unwrap(); 46 | 47 | assert_eq!("2019", &caps["year"]); 48 | assert_eq!("01",&caps["month"]); 49 | assert_eq!("01",&caps["day"]); 50 | 51 | // 使用replace_all方法来替换匹配的字符串。注意制定的格式是以“$”符号和命名捕获变量组合而成的。 52 | let after= re.replace_all("2018-01-01","$month/$day/$year"); 53 | assert_eq!(after, "01/01/2018"); 54 | } 55 | } -------------------------------------------------------------------------------- /src/ch11/await/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "await" 3 | version = "0.1.0" 4 | authors = ["Tony Deng "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dev-dependencies] 10 | futures-preview = {version = "=0.3.0-alpha.17", features = ["async-await", "nightly"]} -------------------------------------------------------------------------------- /src/ch11/await/src/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | 3 | use futures::executor::block_on; 4 | 5 | mod first{ 6 | 7 | use futures::executor::block_on; 8 | 9 | async fn hello_world() { 10 | println!("hello, world!"); 11 | } 12 | 13 | fn main() { 14 | let future = hello_world(); 15 | block_on(future); 16 | } 17 | #[test] 18 | fn run_main(){ 19 | main() 20 | } 21 | } 22 | 23 | #[derive(Debug)] 24 | struct Song; 25 | 26 | async fn learn_song() -> Song { 27 | println!("learn_song {:?} ...",Song); 28 | Song 29 | } 30 | async fn sing_song(_: Song) { 31 | println!("sing_song {:?} ...",Song); 32 | } 33 | async fn dance(){ 34 | println!("dance...") 35 | } 36 | 37 | mod second { 38 | 39 | use super::*; 40 | 41 | fn main() { 42 | let song = block_on(learn_song()); 43 | block_on(sing_song(song)); 44 | block_on(dance()); 45 | } 46 | 47 | #[test] 48 | fn run_second_main() { 49 | main(); 50 | } 51 | 52 | } 53 | 54 | 55 | mod third{ 56 | use super::*; 57 | 58 | async fn learn_and_sing() { 59 | let song = learn_song().await; 60 | sing_song(song).await; 61 | } 62 | 63 | async fn async_main(){ 64 | let f1 = learn_and_sing(); 65 | let f2 = dance(); 66 | 67 | futures::join!(f1, f2); 68 | } 69 | 70 | fn main() { 71 | block_on(async_main()); 72 | } 73 | 74 | #[test] 75 | fn run_main() { 76 | main(); 77 | } 78 | } 79 | // async fn hello_world() { 80 | // println!("Hello, world!"); 81 | // } 82 | 83 | // #[derive(Debug)] 84 | // struct Song { 85 | 86 | // } 87 | 88 | // async fn learn_song() -> Song { 89 | // Song{} 90 | // } 91 | // async fn sing_song(song: Song) { 92 | // Song{} 93 | // } 94 | // async fn dance() { 95 | 96 | // } 97 | 98 | // fn main() { 99 | 100 | 101 | // let future = hello_world(); 102 | // block_on(future); 103 | // } 104 | -------------------------------------------------------------------------------- /src/ch11/mod.rs: -------------------------------------------------------------------------------- 1 | //! 安全并发 2 | //! 3 | 4 | pub fn title() { 5 | println!("第11章: {}","安全并发"); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![doc( 2 | // html_playground_url = "https://play.rust-lang.org/", 3 | // test(on_crate_inject, attr(deny(warnings))), 4 | // test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) 5 | // )] 6 | 7 | pub mod ch01; 8 | pub mod ch10; 9 | pub mod ch11; --------------------------------------------------------------------------------