From b36575bd43eed35afe4d1b6005a374694fe29320 Mon Sep 17 00:00:00 2001 From: KaiserY Date: Mon, 7 Aug 2017 10:18:45 +0800 Subject: [PATCH] update ch20-01 --- src/SUMMARY.md | 14 ++-- ...ch19-05-advanced-functions-and-closures.md | 74 +++++++++++++++++- src/ch20-00-final-project-a-web-server.md | 23 ++++++ src/ch20-01-single-threaded.md | 10 +++ src/img/hello.png | Bin 0 -> 8491 bytes 5 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/ch20-00-final-project-a-web-server.md create mode 100644 src/ch20-01-single-threaded.md create mode 100644 src/img/hello.png diff --git a/src/SUMMARY.md b/src/SUMMARY.md index fbf4553..999df14 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -111,10 +111,10 @@ - [高级类型](ch19-04-advanced-types.md) - [高级函数与闭包](ch19-05-advanced-functions-and-closures.md) -- [Final Project: Building a Multithreaded Web Server](ch20-00-final-project-a-web-server.md) - - [A Single Threaded Web Server](ch20-01-single-threaded.md) - - [How Slow Requests Affect Throughput](ch20-02-slow-requests.md) - - [Designing the Thread Pool Interface](ch20-03-designing-the-interface.md) - - [Creating the Thread Pool and Storing Threads](ch20-04-storing-threads.md) - - [Sending Requests to Threads Via Channels](ch20-05-sending-requests-via-channels.md) - - [Graceful Shutdown and Cleanup](ch20-06-graceful-shutdown-and-cleanup.md) \ No newline at end of file +- [最后的项目: 构建多线程 web server](ch20-00-final-project-a-web-server.md) + - [单线程 web server](ch20-01-single-threaded.md) + - [慢请求如何影响吞吐率](ch20-02-slow-requests.md) + - [设计线程池接口](ch20-03-designing-the-interface.md) + - [创建线程池并储存线程](ch20-04-storing-threads.md) + - [使用通道向线程发送请求](ch20-05-sending-requests-via-channels.md) + - [Graceful Shutdown 与清理](ch20-06-graceful-shutdown-and-cleanup.md) \ No newline at end of file diff --git a/src/ch19-05-advanced-functions-and-closures.md b/src/ch19-05-advanced-functions-and-closures.md index ddfe70f..37374ae 100644 --- a/src/ch19-05-advanced-functions-and-closures.md +++ b/src/ch19-05-advanced-functions-and-closures.md @@ -30,4 +30,76 @@ fn main() { 列表 19-34:使用 `fn` 类型接受函数指针作为参数 -这会打印出 `The answer is: 12`。 \ No newline at end of file +这会打印出 `The answer is: 12`。`do_twice` 中的 `f` 被指定为一个接受一个 `i32` 参数并返回 `i32` 的 `fn`。接着就可以在 `do_twice` 函数体中调用 `f`。在 `main` 中,可以将函数名 `add_one` 作为第一个参数传递给 `do_twice`。 + +不同于闭包,`fn` 是一个类型而不是一个 trait,所以直接指定 `fn` 作为参数而不是声明一个带有 `Fn` 作为 trait bound 的泛型参数。 + +函数指针实现了所有三个闭包 trait(`Fn`、`FnMut` 和 `FnOnce`),所以总是可以在调用期望闭包的函数时传递函数指针作为参数。倾向于编写使用泛型和闭包 trait 的函数,这样它就能接受函数或闭包作为参数。一个只期望接受 `fn` 的情况的例子是与不存在闭包的外部代码交互时:C 语言的函数可以接受函数作为参数,但没有闭包。 + +比如,如果希望使用 `map` 函数将一个数字 vector 转换为一个字符串 vector,就可以使用闭包: + +```rust +let list_of_numbers = vec![1, 2, 3]; +let list_of_strings: Vec = list_of_numbers + .iter() + .map(|i| i.to_string()) + .collect(); +``` + +或者可以将函数作为 `map` 的参数来代替闭包: + +```rust +let list_of_numbers = vec![1, 2, 3]; +let list_of_strings: Vec = list_of_numbers + .iter() + .map(ToString::to_string) + .collect(); +``` + +注意这里必须使用“高级 trait”部分讲到的完全限定语法,因为存在多个叫做 `to_string` 的函数;这里使用定义于 `ToString` trait 的 `to_string` 函数,标准库为所有实现了 `Display` 的类型实现了这个 trait。 + +一些人倾向于函数风格,一些人喜欢闭包。他们最终都会产生同样的代码,所以请使用你更明白的吧。 + +### 返回闭包 + +因为闭包以 trait 的形式体现,返回闭包就有点微妙了;不能直接这么做。对于大部分需要返回 trait 的情况,可以使用是实现了期望返回的 trait 的具体类型替代函数的返回值。但是这不能用于闭包。他们没有一个可返回的具体类型;例如不允许使用函数指针 `fn` 作为返回值类型。 + +这段代码尝试直接返回闭包,它并不能编译: + +```rust +fn returns_closure() -> Fn(i32) -> i32 { + |x| x + 1 +} +``` + +编译器给出的错误是: + +``` +error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static: +std::marker::Sized` is not satisfied + --> :2:25 + | +2 | fn returns_closure() -> Fn(i32) -> i32 { + | ^^^^^^^^^^^^^^ the trait `std::marker::Sized` is + not implemented for `std::ops::Fn(i32) -> i32 + 'static` + | + = note: `std::ops::Fn(i32) -> i32 + 'static` does not have a constant size + known at compile-time + = note: the return type of a function must have a statically known size +``` + +又是 `Sized` trait!Rust 并不知道需要多少空间来储存闭包。不过我们在上一部分见过这种情况的解决办法:可以使用 trait 对象: + +```rust +fn returns_closure() -> Box i32> { + Box::new(|x| x + 1) +} +``` + +关于 trait 对象的更多内容,请参考第十八章。 + +## 总结 + +好的!现在我们学习了 Rust 并不常用但你可能用得着的功能。我们介绍了很多复杂的主题,这样当你在错误信息提示或阅读他人代码时遇到他们时,至少可以说已经见过这些概念和语法了。 + +现在,让我们再开始一个项目,将本书所学的所有内容付与实践! \ No newline at end of file diff --git a/src/ch20-00-final-project-a-web-server.md b/src/ch20-00-final-project-a-web-server.md new file mode 100644 index 0000000..ec93403 --- /dev/null +++ b/src/ch20-00-final-project-a-web-server.md @@ -0,0 +1,23 @@ +# 最后的项目: 构建多线程 web server + +> [ch20-00-final-project-a-web-server.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-00-final-project-a-web-server.md) +>
+> commit 08e50d5e147ad290d88efd5c58365000723626df + +这是一次漫长的旅途,不过我们做到了!这一章便是本书的结束。离别是如此甜蜜的悲伤。不过在我们结束之前,再来一起构建另一个项目,来展示最后几章所学,同时复习更早的章节。 + +下面就是我们将要做的:一个简单的 web server: + +![hello from rust](img/hello.png) + +为此我们将: + +1. 学习一些 TCP 与 HTTP 知识 +2. 在套接字(socket)上监听 TCP 请求 +3. 解析少量的 HTTP 请求 +4. 创建一个合适的 HTTP 响应 +5. 通过线程池改善 server 的吞吐量 + +在开始之前,需要提到一点:如果你曾在生产环境中编写过这样的代码,还有很多更好的做法。特别需要指出的是,crates.io 上提供了很多更完整健壮的 web server 和 线程池实现,要不我们编写的好很多。 + +然而,本章的目的在于学习,而不是走捷径。因为 Rust 是一个系统编程语言,能够选择处理什么层次的抽象。我们能够选择比其他语言可能或可用的层次更低的层次。所以我们将自己编写一个基础的 HTTP server 和线程池,以便学习将来可能用到的 crate 背后的通用理念和技术。 \ No newline at end of file diff --git a/src/ch20-01-single-threaded.md b/src/ch20-01-single-threaded.md new file mode 100644 index 0000000..a845d91 --- /dev/null +++ b/src/ch20-01-single-threaded.md @@ -0,0 +1,10 @@ +## 单线程 web server + +> [ch20-01-single-threaded.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch20-01-single-threaded.md) +>
+> commit 2e269ff82193fd65df8a87c06561d74b51ac02f7 + +首先让我们创建一个可运行的单线程 web server。我们将处理 TCP 和 HTTP 请求和响应的原始字节来从 server 向浏览器发送 HTML。首先先快速了解一下涉及到的协议。 + +**超文本传输协议**(*Hypertext Transfer Protocol*,*HTTP*)驱动着现在的互联网,它构建于**传输控制协议**(*Transmission Control Protocol*,*TCP*)的基础上。这里并不会过多的涉及细节,只做简单的概括:TCP 是一个底层协议,HTTP 是 TCP 之上的高级协议。这两个都是一种被称为**请求-响应协议**(*request-response protocol*)的协议,也就是说,有**客户端**(*client*)来初始化请求,并有**服务端**(*server*)监听请求并向客户端提供响应。请求与响应的内容由协议本身定义。 + diff --git a/src/img/hello.png b/src/img/hello.png new file mode 100644 index 0000000000000000000000000000000000000000..19e2cbc0ac4a1fc44e39f9c470e44b81dfe2f1f4 GIT binary patch literal 8491 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 F{{vktfA9bR literal 0 HcmV?d00001