欢迎来到“每天一个板条箱” ,我的日记是我深入探讨Rust编程世界的日记。作为具有开源背景的JavaScript开发人员,我决定承担为Rust Community重新编写流行的JavaScript包的挑战。
这是我为这个项目实现的一些TL; DR目标:
- ð - 学习生锈和流行的JavaScript模块
- ð�通过出版新的板条
- ð�ð«与您分享我的学习,技巧和最佳实践
如果您来这里学习如何安装,从头开始启动Rust或为IDE/环境设置工具链,请参见the Rust Handbook!否则,让我们潜入!
板条箱#1:koude0
原始软件包:sindresorhus/has-flag
Sindre的JavaScript软件包体现了单一目的和可重复性的概念。通常,时间仅作为单个导出功能实现。 has-flag
在这方面没有什么不同。
审查
此单个功能模块允许开发人员快速检测ARGV中特定标志的存在(参数传递到运行的脚本/二进制文件)。该模块的代码如下:
import process from 'process'; // eslint-disable-line node/prefer-global/process
export default function hasFlag(flag, argv = process.argv) {
const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
const position = argv.indexOf(prefix + flag);
const terminatorPosition = argv.indexOf('--');
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
}
使用此模块,我们可以根据传递到脚本中的内容轻松检查特定参数(“ flag”)。这是README的一个例子:
// foo.js
import hasFlag from 'has-flag';
hasFlag('unicorn');
//=> true
hasFlag('--unicorn');
//=> true
hasFlag('f');
//=> true
hasFlag('-f');
//=> true
hasFlag('foo=bar');
//=> true
hasFlag('foo');
//=> false
hasFlag('rainbow');
//=> false
$ node foo.js -f --unicorn --foo=bar -- --rainbow
方法
为了开始我的旅程,我决定首先制作单位测试。这样,我可以向后工作,直到有解决方案。
学习#1:货物使用约定
Cargo是用于运行测试,安装软件包/依赖项的开箱即用工具,以及更多的公约用于构建库与二进制文件。
默认情况下,货物在工作空间中寻找两个约定之一:
-
库:如果您要构建锈库(“板条箱”),则货物将在工作区中强制执行
src/lib.rs
的存在。 -
二进制文件:如果您要建造生锈的二进制文件,则货物将强制执行
src/main.rs
的存在。
我喜欢这个!每次我打开一个生锈项目时,我都会知道 从哪里开始/开始读取代码。
学习#2:如何编写测试
接下来,我学会了如何在Rust中编写测试。从我查看的所有软件包中,根据Rust Handbook,单位测试均在同一文件中编写 !此外,您会在锈测试中看到宏的用法。 宏是在编译时间创建代码扩展的强大工具,从编写样板代码的行中为您节省了。
以我在模块中写的以下测试为例:
// Macro for setting up a test module
#[cfg(test)]
mod tests {
// Gives access to outer scope (not just the `pub fn`'s)
// great to test functions used in Dependency Injection
use super::*;
// Macro that turns a function into a unit test
#[test]
fn args_with_value_not_matching_double_dash() {
let args = vec!["--foo", "--unicorn=rainbow", "--bar"];
let expected_value = "unicorn=rainbow";
assert!(_has_flag(
args.into_iter().map(ToString::to_string),
expected_value
))
}
}
-
koude4 macro:这是我们设置测试模块的宏。它只有在运行
cargo test
时才指示Rust编译和运行测试代码
-
koude6 macro:此宏将功能转换为单位测试!您将使用
assert!()
或assert_eq!()
之类的宏来验证要测试的代码结果。 -
use super::*
:将外部范围暴露于您的inner test module。地球允许在测试功能中使用的外部范围中定义的任何内容(pub fn
未定义的任何内容)!
学习3:与std::env
合作
实施测试后,我决定在编写此模块时做一个破解。非常感谢Steve Klabnik帮助我获得了功能和设置依赖注入的类型,这是 First 迭代:
pub fn has_flag(flag: &str) -> bool {
_has_flag(std::env::args(), flag)
}
fn _has_flag<I: Iterator<Item = String>>(mut args: I, flag: &str) -> bool {
let prefix = if flag.starts_with('-') {
""
} else {
if flag.len() == 1 {
"-"
} else {
"--"
}
};
let position = args.position(|arg| arg == format!("{}{}", prefix, flag));
let terminator_position = args.position(|arg| arg == "--");
position.is_some() && (!terminator_position.is_some() || position < terminator_position)
}
-
koude12:这是您可以访问
argv
或该程序开始的论点的方式。std
是Rust standard library,默认情况下可用于所有Rust板条。 -
koude15:在迭代器中搜索元素,并返回其索引。我认为这与JavaScript中的
.indexOf
相当。 但是,我在代码中犯了一些错误,我将解释为什么。 -
koude17 & koude18:Rust中没有
null
的概念。相反,选项是一种旨在处理返回的无值的类型。在我们的代码的一些部分中,我们不在乎索引是什么,而不是索引存在。.is_some()
是这里的理想用途。
学习4:可变性和错误!
我最初发布和发布的此代码实际上有一些错误!,但是,我的初始测试正在通过。我创建了一个GitHub Issue,当我有时间并实现has-flag
的测试套件时回来。
我的PR introducing the full tests失败了,所以我知道代码本身必须有一个错误。在The Rust Programming Language Discord上的“初学者”频道获得了一些反馈之后,我开始意识到自己犯了一些错误。这是通过测试的更新代码:
pub fn has_flag(flag: &str) -> bool {
_has_flag(std::env::args(), flag)
}
fn _has_flag<I: Iterator<Item = String>>(args: I, flag: &str) -> bool {
let prefix = if flag.starts_with('-') {
""
} else if flag.len() == 1 {
"-"
} else {
"--"
};
let formatted_flag = format!("{}{}", prefix, flag);
args.take_while(|arg| arg != "--")
.any(|arg| arg == formatted_flag)
}
-
mut args
=>args
:第一个代码气味我应该在写第一次迭代时注意到,Rust Compiler迫使我在我的args
参数旁边添加mut
,以便功能。这对我没有意义,因为仅当我正在突变args
时才需要mut
。 -
.position()
突变:这导致我注意到.position()
是 我想使用的功能。.position()
在迭代器中消耗项目,直到匹配谓词为止。因此,确定terminator_position
的代码实际上是访问了突变的args
。我的所有涉及--
Args终结器的测试都失败了! -
koude34:采用一个迭代器,并通过它运行 谓词为真,然后返回 是这些项目的迭代器,直到谓词为止。 我想到的类似于迭代器 filter 。这是我们在这里使用的完美选择,因为我们不想匹配
--
终结者之后通过的ARGS。它也大大简化了我们的代码!
运送ITð
这种迭代通过了我们的所有测试,我能够愉快地合并并发布new release of koude0。当我继续进行work on this journal时,我将遇到许多有关Rust的挑战和学习,我迫不及待地想与您分享它们。