- 原文地址:A Simple Web App in Rust, Part 1
- 原文做者:Joel's Journal
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:LeopPro
- 校對者:pthtc hippyk
站在一個經驗豐富但剛接觸本生態系統的開發者的角度,使用 Rust 開發一個小型的 Web 應用是什麼感受呢?請繼續閱讀。html
我第一次據說 Rust 的時候就對它產生了興趣。一個支持宏的系統級語言,而且在高級抽象方面有成長空間。真棒!前端
到目前爲止,我只寫過關於 Rust 的博客,作了一些很基礎的「Hello World」級程序。因此,我估計個人觀點會欠一些火候。android
不久以前,我看見了關於學習 Racket 的這篇文章,我以爲特別好。咱們須要更多的人分享他們做爲技術初學者時得到的經驗,尤爲是那些已經有至關豐富的技術經驗的人[1]。我也很是喜歡它的「思惟流」方法。我想,像這樣寫一個 Rust 教程,應該是一個很是好的嘗試。ios
好了,前言說完了,咱們開始吧!git
我想構建的應用要實現個人一個簡單需求:用一種無腦的方式記錄我天天服藥時間。我想我點一下主屏幕上的連接,讓它記錄此次訪問,而且這將會儲存爲一份我服藥時間的記錄。github
Rust 彷佛很適合這個應用。它速度快,運行一個簡單的服務器消耗的資源特別少,因此它不會對個人 VPS 形成負擔。我還想用 Rust 作一些更實際的事。web
最小可行性版本很是小巧,但若是我想添加更多功能,它也有增加空間。聽起來完美!後端
我不得不認可一件事:我弄丟了這個項目的早期版本,這將產生如下弊端:當我重現它的時候,我並不會有幾周前剛剛接觸它的時候那種陌生感。然而,我想我仍然記得當時讓我痛苦的地方,而且我會盡力重現這些難點。瀏覽器
我知道一個道理有必要在這裏講一下:對於一個獨立的我的程序來講,利用現有 API 要比試着獨立完成全部的工做容易得多。bash
爲了達成目的,我制定了以下計劃:
因此,我要創建一個新的 Git 倉庫 & 裝好 homebrew。我至少知道,我先要安裝 Rust。
$ brew update
...
$ brew install rust
==> Downloading https://homebrew.bintray.com/bottles/rust-1.0.0.yosemite.bottle.tar.gz
############################################################ 100.0%
==> Pouring rust-1.0.0.yosemite.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
/usr/local/etc/bash_completion.d
zsh completion has been installed to:
/usr/local/share/zsh/site-functions
==> Summary
/usr/local/Cellar/rust/1.0.0: 13947 files, 353M
複製代碼
Ok,在開始以前,咱們先寫一個常規的「Hello World」程序。
$ cat > hello_world.rs
fn main() {
println!("hello world");
}
^D
$ rustc hello_world.rs
$ ./hello_world
hello world
$
複製代碼
到目前爲止一切順利。Rust 正常工做了,或者至少說,Rust 的編譯器在正常工做。
有位朋友建議我嘗試使用 nickle.rs,那是 Rust 的 一個 Web 應用框架。我以爲不錯。
截止到今天,它的第一個示例是:
#[macro_use] extern crate nickel;
use nickel::Nickel;
fn main() {
let mut server = Nickel::new();
server.utilize(router! {
get "**" => |_req, _res| {
"Hello world!"
}
});
server.listen("127.0.0.1:6767");
}
複製代碼
我第一次作這些的時候,我有一點小分心,去學了一點 Cargo。此次我注意到了這個入門指南,因此我打算跟着它走而不是什麼都靠本身誤打誤撞。
這裏有一個腳本,我應該經過 curl
下載而後使用 root 權限執行。可是「患有強迫症的」我打算先把腳本下載下來檢查一下。
curl -LO https://static.rust-lang.org/rustup.sh
Ok,這事實上並不像我預想的那樣,這個腳本完成了不少工做,大部分都是我如今不想本身去作的。而我很想知道,cargo
是否是用 rustc
來安裝的?
$ which cargo
/usr/local/bin/cargo
$ cargo -v
Rust 包管理器
用法:
cargo <命令> [<參數>...]
cargo [選項]
選項:
-h, --help 顯示幫助信息
-V, --version 顯示版本信息並退出
--list 安裝命令列表
-v, --verbose 使用詳細的輸出
常見的 cargo 命令:
build 編譯當前工程
clean 刪除目標目錄
doc 編譯此工程及其依賴項文檔
new 建立一個新的 cargo 工程
run 編譯並執行 src/main.rs
test 運行測試
bench 運行基準測試
update 更新 Cargo.lock 中的依賴項
search 搜索註冊過的 crates
執行 'cargo help <command>' 獲取指定命令的更多幫助信息。
複製代碼
Ok,我猜這看起來不錯吧?我如今就開始用它。
$ rm rustup.sh
下一步是生成一個新的項目目錄,可是我已經有了一個項目目錄。無論怎樣,我仍是要試一試。
$ cargo new . --bin
目標 `/Users/joel/Projects/simplelog/.` 已經存在
複製代碼
嗯……它不工做。
$ cargo -h
在 <路徑> 處建立一個新的 Cargo 包。
用法:
cargo new [選項] <路徑>
cargo new -h | --help
選項:
-h, --help 顯示幫助信息
--vcs <vcs> 爲指定的版本管理系統(git 或 hg)
初始化一個新倉庫
或者不使用版本管理系統(none)
--bin 建立可執行文件工程而不是庫工程
--name <name> 設置結果包名
-v, --verbose 使用詳細的輸出
複製代碼
上述代碼第一行中
cargo -h
應爲做者筆誤,實爲cargo new -h
。(譯者注)
嗯,它彷佛不會按照個人預想去工做,我須要重建這個倉庫。
$ cd ../
$ rm -rf simplelog/
$ cargo new simple-log --bin
$ cd simple-log/
複製代碼
Ok,咱們看看這裏有什麼?
$ tree
.
|____.git
| |____config
| |____description
| |____HEAD
| |____hooks
| | |____README.sample
| |____info
| | |____exclude
| |____objects
| | |____info
| | |____pack
| |____refs
| | |____heads
| | |____tags
|____.gitignore
|____Cargo.toml
|____src
| |____main.rs
複製代碼
看,它創建了一個 Git 倉庫,Cargo.toml
文件和在 src
目錄中的 main.rs
文件,看起來不錯。
根據 Nickel 的入門指南,我向 Cargo.toml
文件中加入 nickel.rs
依賴,如今它看起來像是這樣:
[package]
name = "simple-log"
version = "0.1.0"
authors = ["Joel McCracken <mccracken.joel@gmail.com>"]
[dependencies.nickel]
git = "https://github.com/nickel-org/nickel.rs.git"
複製代碼
我以爲這很容易理解。然而我不肯定 dependencies.nickel
實際的含義是什麼。dependencies
是一個帶有 nickel
鍵的哈希值麼?但能夠確定的是,咱們已經在工程中引進 Nickel 了,真棒!
管他呢,我把那個例子複製到 main.rs
中:
#[macro_use] extern crate nickel;
use nickel::Nickel;
fn main() {
let mut server = Nickel::new();
server.utilize(router! {
get "**" => |_req, _res| {
"Hello world!"
}
});
server.listen("127.0.0.1:6767");
}
複製代碼
啥?macro_use
、extern
都是什麼東西?爲何要用 use
?這些疑問我會在下面一一解答。
這裏我有一些疑問,macro_use
彷佛是一個宏指令[2],可是我沒有看到任何宏調用,因此我刪除了它。然而如今我注意到了 router!
,所以我更加傾向於這是一個宏指令。同時,我懷疑用指令
一詞來指代是否恰當,所以別太確定。
咱們試一下:
cargo run
Updating git repository `https://github.com/nickel-org/nickel.rs.git`
Updating registry `https://github.com/rust-lang/crates.io-index`
Downloading unsafe-any v0.4.1
Downloading httparse v0.1.2
Downloading traitobject v0.0.3
Downloading lazy_static v0.1.10
Downloading pkg-config v0.3.4
Downloading num_cpus v0.2.5
Downloading modifier v0.1.0
Downloading groupable v0.2.0
Downloading unicase v0.1.0
Downloading gcc v0.3.5
Downloading time v0.1.25
Downloading log v0.3.1
Downloading url v0.2.34
Downloading plugin v0.2.6
Downloading hyper v0.4.0
Downloading matches v0.1.2
Downloading mustache v0.6.1
Downloading bitflags v0.1.1
Downloading typeable v0.1.1
Downloading openssl v0.6.2
Downloading rustc-serialize v0.3.14
Downloading typemap v0.3.2
Downloading regex v0.1.30
Downloading cookie v0.1.20
Downloading mime v0.0.11
Downloading libc v0.1.8
Downloading openssl-sys v0.6.2
Compiling modifier v0.1.0
Compiling traitobject v0.0.3
Compiling regex v0.1.30
Compiling libc v0.1.8
Compiling lazy_static v0.1.10
Compiling matches v0.1.2
Compiling httparse v0.1.2
Compiling rustc-serialize v0.3.14
Compiling groupable v0.2.0
Compiling pkg-config v0.3.4
Compiling gcc v0.3.5
Compiling bitflags v0.1.1
Compiling unicase v0.1.0
Compiling typeable v0.1.1
Compiling unsafe-any v0.4.1
Compiling log v0.3.1
Compiling num_cpus v0.2.5
Compiling typemap v0.3.2
Compiling mime v0.0.11
Compiling plugin v0.2.6
Compiling openssl-sys v0.6.2
Compiling time v0.1.25
Compiling openssl v0.6.2
Compiling url v0.2.34
Compiling mustache v0.6.1
Compiling cookie v0.1.20
Compiling hyper v0.4.0
Compiling nickel v0.5.0 (https://github.com/nickel-org/nickel.rs.git#69546f58)
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
Running `target/debug/simple-log`
Listening on http://127.0.0.1:6767
Ctrl-C to shutdown server
^C
複製代碼
哦吼!個人瀏覽器中 localhost:6767
的訪問成功啦!
Ok,如今我想嘗試一件事情,而後今晚就收工:我能夠將「Hello World」移動到它本身的函數中麼?畢竟咱們如今是嬰兒學步的階段。
fn say_hello() {
"Hello dear world!";
}
fn main() {
let mut server = Nickel::new();
server.utilize(router! {
get "**" => |_req, _res| {
say_hello();
}
});
server.listen("127.0.0.1:6767");
}
複製代碼
錯誤……當我此次運行的時候,我看到了「未找到」。咱們此次把分號去掉,以防萬一:
fn say_hello() {
"Hello dear world!"
}
fn main() {
let mut server = Nickel::new();
server.utilize(router! {
get "**" => |_req, _res| {
say_hello()
}
});
server.listen("127.0.0.1:6767");
}
複製代碼
好吧……如今編譯器報出了不一樣的錯誤信息:
$ cargo run
Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log)
src/main.rs:6:5: 6:24 錯誤:不匹配的類型:
預期 `()`,
找到 `&'static str` (預期 (), 找到 &-ptr) [E0308] src/main.rs:6 "Hello dear world!" ^~~~~~~~~~~~~~~~~~~ 錯誤:因爲先前的錯誤而停止 不能編譯 `simple-log`。 想查看更多信息,請加上 --verbose 從新運行命令。 複製代碼
根據報錯信息,我猜想分號的有無是重要的。如今這產生了一個類型錯誤。哦,我有九成的把握確定這裏的 ()
指的是「unit」,這是 Rust 中的空、未定義、或者未規定。我知道這不徹底對,可是我想這是講得通的。
我假設 Rust 會作類型推斷。編譯器沒這麼作嗎?仍是隻在函數邊界附近沒有作?嗯……
錯誤信息告訴我,編譯器但願函數的返回值是「unit」,可是實際上返回值是一個靜態字符串(這是啥?)。我已經看過函數返回值的語法了,咱們看一看:
#[macro_use] extern crate nickel;
use nickel::Nickel;
fn say_hello() -> &'static str { "Hello dear world!" } fn main() { let mut server = Nickel::new(); server.utilize(router! { get "**" => |_req, _res| { say_hello() } }); server.listen("127.0.0.1:6767"); } 複製代碼
在我看來 &'static str
類型很是的怪異。它會成功編譯麼?它會正常工做麼?
$ cargo run &
[1] 14997
Running `target/debug/simple-log`
Listening on http://127.0.0.1:6767
Ctrl-C to shutdown server
$ curl http://localhost:6767
Hello dear world!
$ fg
cargo run
^C
複製代碼
耶,它工做了!這一次 Rust 沒有使人失望。我不知道是否是由於我對這些工具更熟悉了,仍是我選擇去多看文檔,可是我樂在其中。讀一門語言和寫一門語言之間的差異很是的驚奇。雖然我理解這些代碼示例,可是我仍然不能高效的編輯它們。
—
在下一章中,咱們將完成當前日期寫入文件的過程。你能夠在這裏閱讀它。
—
系列文章:使用 Rust 開發一個簡單的 Web 應用
[1] 我並非想說,初學者的經驗是沒有價值的 —— 遠非如此!我認爲相比於經驗豐富者而言,初學者常常會帶來一些獨到的看法,他們可能會注意到生態系統中的某些東西是非標準的。
[2] 我一般說編譯期指令,可是這對於 Rust 這樣一個編譯語言來講沒太大意義。因此除了宏指令之外,我不知道該如何表述它了。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。