从零开始学习《Rust程序设计》Day1-Chapter1.1-2.5/导论及搭建web服务器
写在开头
我在学习Rust之前在嵌入式领域做了6年的C/C++开发,并轻量使用过Python。最近购入该书学习Rust,开帖记录学习过程遇到的问题及解决方案。

开发环境介绍
操作系统为MacOS,CPU为M2 pro,使用Clion 2025.1.7作为编辑器,使用rustc 1.91.1版本。
该书英文版为2021年出版,中文版于2023年出版。书中示例代码版本过旧,在实操中碰到了一些问题,我将会在帖子中记录解决方案。有的问题我并不清楚确切的原因,欢迎讨论。
问题记录
- Chapter2.5搭建web服务器问题记录
书中原版代码如下
Cargo.toml
[package]
name = "actix-gcd"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "1.0.8"
serde = \{ version = "1.0", features = ["derive"]}
main.rs
use actix_web::{web, App, HttpServer, HttpResponse};
use serde::Deserialize;
fn get_index() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/html")
.body(
r#"
<title>GCD Caculator</title>
<form action="/gcd" method="post">
<input type="text" name="n"/>
<input type="text" name="m"/>
<button type="submit">GCD Caculator</button>
</form>
"#,
)
}
fn gcd(mut n:u64, mut m:u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m;
m = n;
n = t;
}
m = m % n;
}
n
}
#[derive(Deserialize)]
struct GcdParams {
n:u64,
m:u64,
}
fn post_gcd(form: web::Form<GcdParams>) -> HttpResponse {
if form.n == 0 || form.m == 0 {
return HttpResponse::BadRequest()
.content_type("text/html")
.body("Computing the GCD with zero is boring.");
}
let response =
format!("The greatest common divisor of the numbers {} and {} \
is <b>{}<b>\n", form.n, form.m, gcd(form.n, form.m));
HttpResponse::Ok()
.content_type("text/html")
.body(response)
}
fn main() {
let server = HttpServer::new(|| {
App::new()
.route("/", web::get().to(get_index))
.route("/gcd", web::post().to(post_gcd))
});
println!("Serving on http://localhost:3000...");
server
.bind("127.0.0.1:3000").expect("Failed to bind server")
.run().expect("error running server");
}
调试过程:
- 编译运行时报错
error[E0277]: cannot subtract `TimeDelta` from `Tm`
--> /Users/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/actix-http-0.2.11/src/cookie/jar.rs:226:44
|
226 | cookie.set_expires(time::now() - Duration::days(365));
| ^ no implementation for `Tm - TimeDelta`
|
= help: the trait `Sub<TimeDelta>` is not implemented for `Tm`
= help: the following other types implement trait `Sub<Rhs>`:
`Tm` implements `Sub<time::Duration>`
`Tm` implements `Sub`
解决方案
更新库版本至最新
Cargo.toml
[dependencies]
actix-web = "4.12.1"
serde = { version = "1.0.228", features = ["derive"]}
- 更新后产生新的错误
error[E0277]: the trait bound `fn() -> HttpResponse {get_index}: Handler<_>` is not satisfied
--> src/main.rs:55:39
|
55 | .route("/", web::get().to(get_index))
| -- ^^^^^^^^^ the trait `Handler<_>` is not implemented for fn item `fn() -> HttpResponse {get_index}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `Route::to`
--> /Users/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/actix-web-4.12.1/src/route.rs:212:12
|
210 | pub fn to<F, Args>(mut self, handler: F) -> Self
| -- required by a bound in this associated function
211 | where
212 | F: Handler<Args>,
| ^^^^^^^^^^^^^ required by this bound in `Route::to`
该问题是由于新版actix_web不兼容老代码,需要做出以下修改:
函数需要用async修饰,例如fn get_index()==>async fn get_index()。修改get_index和post_gcd即可。
- 修改后又出现新问题
error[E0599]: no method named `expect` found for struct `Server` in the current scope
--> src/main.rs:61:16
|
59 | / server
60 | | .bind("127.0.0.1:3000").expect("Failed to bind server")
| | ---------------------- method `expect` is available on `Result<HttpServer<{closure@src/main.rs:53:34: 53:36}, App<actix_web::app_service::AppEntry>, actix_web::app_service::AppInit<actix_web::app_service::AppEntry, BoxBody>, BoxBody>, std::io::Error>`
61 | | .run().expect("error running server");
| | -^^^^^^ method not found in `Server`
| |_______________|
|
该问题同样是由于新版actix_web不兼容老代码,需要删除run后的expect:run().expect("error running server")==>run()
-
修改后出现warning
warning: unused `Server` that must be used
根据编译器提示,修改sever==>_ = sever -
编译成功但服务器无法进入listen状态
4.12.1版actix-web要求server run之后添加await使其进入listen状态
修改代码.run()==>.run().await -
编译再次报错
提示
error[E0728]: `await` is only allowed inside `async` functions and blocks
根据提示,main函数也需要async修饰 -
提示main函数不允许添加async
error[E0752]: `main` function is not allowed to be `async`.
根据actix-web文档说明,需要在main函数前添加属性
#[actix_web::main]
至此问题全部解决,可以在本地访问网页
附最终版代码
cargo.toml
[package]
name = "activ-gcd"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.12.1"
serde = { version = "1.0.228", features = ["derive"]}
main.rs
use actix_web::{web, App, HttpServer, HttpResponse};
use serde::Deserialize;
async fn get_index() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/html")
.body(
r#"
<title>GCD Caculator</title>
<form action="/gcd" method="post">
<input type="text" name="n"/>
<input type="text" name="m"/>
<button type="submit">GCD Caculator</button>
</form>
"#,
)
}
fn gcd(mut n:u64, mut m:u64) -> u64 {
assert!(n != 0 && m != 0);
while m != 0 {
if m < n {
let t = m;
m = n;
n = t;
}
m = m % n;
}
n
}
#[derive(Deserialize)]
struct GcdParams {
n:u64,
m:u64,
}
async fn post_gcd(form: web::Form<GcdParams>) -> HttpResponse {
if form.n == 0 || form.m == 0 {
return HttpResponse::BadRequest()
.content_type("text/html")
.body("Computing the GCD with zero is boring.");
}
let response =
format!("The greatest common divisor of the numbers {} and {} \
is <b>{}<b>\n", form.n, form.m, gcd(form.n, form.m));
HttpResponse::Ok()
.content_type("text/html")
.body(response)
}
#[actix_web::main]
async fn main() {
let server = HttpServer::new(|| {
App::new()
.route("/", web::get().to(get_index))
.route("/gcd", web::post().to(post_gcd))
});
println!("Serving on http://localhost:3000...");
_ = server
.bind("127.0.0.1:3000").expect("Failed to bind server")
.run().await;
}
浙公网安备 33010602011771号