• A Simple Web App in Rust, Part 1
  • Joel’s Journal
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: LeopPro
  • Proofread by: PTHTC Hippyk

Developing a Simple Web application with Rust, Part 1

1 Introduction & Background

From the perspective of an experienced developer new to the ecosystem, what is it like to develop a small Web application using Rust? Read on.

I became interested in Rust when I first heard of it. A system-level language that supports macros and has room for growth in high-level abstractions. That’s great!

So far, I’ve only blogged about Rust and made some very basic “Hello World” programs. So, I guess my point is a little bit underheated.

Not long ago, I saw this article about learning Racket, and I felt very good about it. We need more people to share their experiences gained as technology beginners, especially those who already have considerable technical experience [1]. I also really like its “flow of thought” approach. I thought it would be a good idea to write a Rust tutorial like this.

All right, that’s the preface, let’s get started!

2 application

I wanted to build an app that fulfilled one of my simple needs: a mindless way to track the time I took my medication each day. I think I’ll click on the link on the home screen and have it record the visit, and it will be stored as a record of how long I took the medication.

Rust seems like a good fit. It’s fast and uses very few resources to run a simple server, so it doesn’t burden my VPS. I also want to do something more practical with Rust.

The minimum viable version is pretty small, but it has room to grow if I want to add more features. Sounds perfect!

3 plan

I have to admit one thing: I lost an early version of the project, which has the following drawbacks: When I recreate it, I don’t feel as strange as WHEN I first touched it a few weeks ago. However, I think I still remember what was painful for me at that time, and I will try my best to recreate those difficult points.

I know a point worth mentioning here: it’s much easier for a standalone individual program to leverage an existing API than to try to do everything on its own.

To achieve this, I made the following plan:

  1. Build a simple Web server that displays “Hello World” on the screen when I visit it.
  2. Build a small program that records the current time in a format every time it runs.
  3. Combine the above two into one program.
  4. Deploy this application on my Digital Ocean VPS.

4 write a “Hello World” Web application

So, I’m going to create a new Git repository & install Homebrew. I at least know that I’m going to install Rust first.

4.1 installation 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 had 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, 353 mCopy the code

Ok, before we start, let’s write a regular “Hello World” program.

$ cat > hello_world.rs
fn main() { println! ("hello world");
}
^D
$ rustc hello_world.rs
$ ./hello_world
hello world
$
Copy the code

So far so good. Rust is working, or at least Rust’s compiler is working.

A friend suggested I try nickle.rs, a Web application framework from Rust. I think it’s good.

As of today, its first example is:

#[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");
}
Copy the code

The first time I did this, I got a little distracted and went to learn a little Cargo. This time I noticed the how-to guide, so I decided to follow it instead of doing everything by mistake.

Here is a script that I should download with curl and execute with root. But “ocD” I’m going to download the script first and check it out.

curl -LO https://static.rust-lang.org/rustup.sh

Ok, this is actually not what I expected, this script does a lot of work, most of which I don’t want to do on my own right now. And I wonder, did Cargo use RUSTC to install it?

$ which cargo
/usr/local/bin/cargo $cargo -v Rust package manager Cargo [options] Options: -h, --help displays help information -v, --version Displays version information and exits --list Installation command list -v, --verbose Displays cargo commands in detail: Build build current project clean Remove target directory doc compile this project and its dependencies document new Create a new cargo project run compile and execute SRC /main.rstestRun the bench run benchmark Update to update the dependency search in Cargo. Lock to search for registered crates executions'cargo help <command>'Get more help on the specified command.Copy the code

Ok, I guess this looks good? I’ll start using it now.

$ rm rustup.sh

4.2 Setting Project

The next step is to generate a new project directory, but I already have one. I’ll give it a try anyway.

$cargo new. - bin target ` / Users/Joel/Projects/simplelog /. ` already existsCopy the code

HMM… It doesn’t work.

$cargo -h creates a new cargo package at < path >. Usage: cargo new [option] < path > cargo new - h | -- help options: -h, --help displays help information -- VCS < VCS > Initializes a new repository for the specified version management system (git or HG) or does not use a version management system (None) --bin creates an executable project instead of a library project --name <name> sets the result package name -v, --verbose uses verbose outputCopy the code

Cargo NEW-H in the first line of the above code is cargo new-H. (Translator’s Note)

Well, it doesn’t seem to be working as I expected, and I need to rebuild the warehouse.

$ cd ../
$ rm -rf simplelog/
$ cargo new simple-log --bin
$ cd simple-log/
Copy the code

Ok, let’s see what we have here?

$ tree
.
|____.git
| |____config
| |____description
| |____HEAD
| |____hooks
| | |____README.sample
| |____info
| | |____exclude
| |____objects
| | |____info
| | |____pack
| |____refs
| | |____heads
| | |____tags
|____.gitignore
|____Cargo.toml
|____src
| |____main.rs
Copy the code

Look, it has a Git repository, the Cargo. Toml file and the main.rs file in the SRC directory. It looks good.

Following Nickel’s introductory guide, I added the nickel.rs dependency to the Cargo. Toml file, and it now looks something like this:

[package]
name = "simple-log"
version = "0.1.0 from"
authors = ["Joel McCracken <[email protected]>"]

[dependencies.nickel]

git = "https://github.com/nickel-org/nickel.rs.git"
Copy the code

I think it’s easy to understand. However, I am not sure what dependencies. Nickel actually means. Is dependencies a hash with a nickel key? But sure enough, we’ve got Nickel in the works, great!

4.3 Running the Hello World example

Anyway, I copied that example into 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");
}
Copy the code

What? What are extern and macro_use? Why use use? I will answer these questions below.

I have some questions here, macro_use seems to be a macro instruction [2], but I don’t see any macro calls, so I removed it. However, now I notice the router! So I’m more inclined to say that this is a macro instruction. At the same time, I doubt the term directive is appropriate, so don’t be too sure.

Let’s try:

cargo run Updating git repository `https://github.com/nickel-org/nickel.rs.git` Updating registry 'https://github.com/rust-lang/crates.io-index' Downloading -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 Modify V0.1.0 Downloading GroupAble v0.2.0 Downloading unicase v0.1.0 Downloading GCC v0.3.5 Downloading Time V0.1.25  DownloadinglogV0.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 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 -any v0.4.1 CompilinglogV0.3.1 Compiling num_cpus v0.2.5 Compiling typemap v0.3.2 Compiling MIME v0.0.11 Compiling plugin v0.2.6 Compiling 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 The Compiling cookie v0.1.20 Compiling the hyper v0.4.0 Compiling nickel v0.5.0 (https://github.com/nickel-org/nickel.rs.git#69546f58)The 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 ^CCopy the code

Oh roar! Localhost :6767 successfully accessed in my browser.

4.4 Final Challenge

Ok, now I want to try one thing, and I’ll call it a night: can I move “Hello World” into its own function? We are, after all, toddlers.

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");
}
Copy the code

Error…… When I run this time, I see “not found”. Let’s remove the semicolon this time just in case:

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");
}
Copy the code

Well… The compiler now displays a different error message:

$cargo run the Compiling simple - log v0.1.0 (file:///Users/joel/Projects/simple-log) SRC/main. Rs: ": 6:24 Error: Mismatched type: expected '()', found '&'static STR '(expected (), found &-ptr) [E0308] SRC /main.rs:6 "Hello dear world!" ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ error: suspended due to previous errors Cannot compile ` simple - log `. For more information, add the --verbose rerun command.Copy the code

Based on the error message, I guess the presence or absence of semicolons is important. This now produces a type error. Oh, I’m 90% sure that the () here refers to “unit,” which is empty, undefined, or undefined in Rust. I know it’s not exactly true, but I think it makes sense.

I assume Rust does type inference. Doesn’t the compiler do that? Or is it just not done near the boundary of the function? HMM…

The error message tells me that the compiler expects the function to return “unit”, but the return value is actually a static string (what is that?). . I’ve looked at the syntax for the return value of a function. Let’s look at it:

#[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"); }Copy the code

The &’static STR type is very weird in my opinion. Will it compile successfully? Will it work?

$ 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
Copy the code

Yeah, it works! This time Rust did not disappoint. I don’t know if it’s because I’m more familiar with the tools, or if I choose to document more, but I enjoy it. The difference between reading a language and writing a language is amazing. While I understand the code examples, I still can’t edit them efficiently.

In the next chapter, we will complete the process of writing the current date to the file. You can read it here.

Article series: Developing a Simple Web application using Rust

  • Part 1
  • The second part a
  • The second part b
  • Part 3
  • Part 4
  • conclusion

Footnote:

[1] I do not mean to suggest that beginner’s experience is worthless — far from it! I think beginners often bring some unique insights than more experienced people, and they may notice that something in the ecosystem is non-standard.

[2] I usually say compile-time instructions, but that doesn’t mean much to a compiled language like Rust. So I don’t know how to express it other than macro instructions.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.