通过深入了解 Rust 的未来

2021-07-26 07:28:24

所以!铁锈期货!简单的豌豆柠檬汁。直到它不是。所以让我们做简单的事情,然后与其等待困难的事情偷袭我们,我们会故意去做。我们安装cargo-edit,以防万一我们还没有,所以我们可以只用cargo addlater: $ cargo install cargo-edit 更新 crates.io index 下载的cart-edit v0.7.0 0.47 秒内下载 1 个板条箱 (57.6 KB)忽略包`cargo-edit v0.7.0`已经安装了,用--force覆盖呀,因为真的很方便。读者只会感到困惑,因为cargo new、cargo build、cargo test、cargo run 等子命令内置在cargo 中,但cargo add 不是。啊对了!事实上,我看到有很多这样的,比如cargo-hack、cargo-udeps、cargo-expand……不胜枚举。然后我们选择一个异步运行时,因为这些期货不会轮询它们自己......我们将毫无理由地选择 tokio,除了:这就是我过去几个月一直在使用的一堆东西。 $ cargo add [email protected] --features full 更新'https://github.com/rust-lang/crates.io-index' index 将 tokio v1.9.0 添加到具有以下特性的依赖项:["full"]

然后我们更改我们的 main 以便它使用默认的 tokio 执行器(cargo newgenerated 对我们来说是一个,但在这里还不够): // 在 `src/main.rs` # [tokio ::main ]async fn main ( ) {打印! ( "Hello from a (so far完全不必要) async runtime") ;} $cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running `target/debug/waytoodeep`Hello from a(到目前为止完全没有必要) ) async runtime 但是让我们在我的项目中添加一些我喜欢的其他好东西。首先,对于错误处理——我们正在编写一个应用程序,我们将从不同的库中获得一堆不同的类型,如果我们可以有一个类型来统一它们,那就太好了。现在我们需要安装 color-eyre 作为默认的恐慌处理程序,我偷偷地修改了一些环境变量,所以我们默认得到回溯。使用 color_eyre ::Report ; # [tokio ::main ]async fn main ( ) -> 结果 < ( ), Report > { setup ( )? ;打印! (“你好,来自(到目前为止完全没有必要的)异步运行时”); Ok ( ( ) )} fn setup ( ) -> Result < ( ), Report > { if std ::env :: var ("RUST_LIB_BACKTRACE" ) 。 is_err ( ) { std ::env :: set_var ( "RUST_LIB_BACKTRACE", "1" ) } color_eyre :: install ( )? ;行 ( ( ) )}

$cargo run 在 0.02 秒内完成了 dev [unoptimized + debuginfo] target(s) 运行 `target/debug/waytoodeep`Hello 从(到目前为止完全不必要的)异步运行时好的好!现在,如果我们在某个地方出现错误,我们将看到完整的堆栈跟踪,如下所示: 最后,因为我喜欢我的日志结构化,让我们添加跟踪并在终端中用漂亮的颜色打印它们,让我们添加跟踪订阅者. $ cargo add [email protected] [email protected] 更新“https://github.com/rust-lang/crates.io-index”索引 将tracing v0.1.26 添加到依赖项 添加tracing-subscriber v0.2.19到依赖项我们已经有了一个设置函数,所以我们只需要在那里安装跟踪订阅者......我们将改变那个 println!到一个信息!。此外,还有一些环境变量操作,如果没有设置,我们默认为所有板条箱的信息日志级别。使用 color_eyre ::Report ;使用追踪::信息; # [tokio ::main ]async fn main ( ) -> 结果 < ( ), Report > { setup ( )? ;信息! (“你好,来自我们为自己做的舒适的巢穴”); Ok ( ( ) )} fn setup ( ) -> Result < ( ), Report > { if std ::env :: var ("RUST_LIB_BACKTRACE" ) 。 is_err ( ) { std ::env :: set_var ( "RUST_LIB_BACKTRACE", "1" ) } color_eyre :: install ( )? ;如果 std ::env :: var ("RUST_LOG") 。 is_err ( ) { std ::env :: set_var ( "RUST_LOG", "info" ) } tracking_subscriber ::fmt :: fmt () 。 with_env_filter(EnvFilter::from_default_env())。在里面 ( ) ; Ok ( ( ) )} $ cargo run 在 0.02 秒内完成开发 [未优化 + 调试信息] 目标运行 `target/debug/waytoodeep`Jul 25 17:03:46.993 INFO waytoodeep:你好来自我们制作的舒适巢穴为我们自己

在决定在喝咖啡休息时阅读哪篇文章时,人们通常会同时打开几个网站,然后阅读最先加载的文章。这是事实。你可以引用我的话,因为,谁会去验证呢?这听起来像很多工作。请相信我。你猜到了!让我们引入 reqwest - 尽管我不喜欢它的 API,但它可以很好地与我们堆栈中的其余部分一起使用。 $ cargo add [email protected] --no-default-features --features rustls-tls 更新“https://github.com/rust-lang/crates.io-index”索引 将 reqwest v0.11.4 添加到依赖项中特征: ["rustls-tls"] # [tokio ::main ]async fn main ( ) -> 结果 < ( ), Report > { setup ( )? ;信息! (“你好,来自我们为自己做的舒适的巢穴”);让客户=客户::新();让 url = "https://fasterthanli.me" ; // 这会将非 200 HTTP 状态代码转换为 rust 错误, // 所以第一个 `?` 传播“我们有一个连接问题”, // 第二个 `?` 传播“我们与服务器聊天,他们// 不高兴" let res = client .获取(网址)。发送 ( ) .await? . error_for_status ()? ;信息! (%url, content_type = ?res.headers ().get ("content-type"), "得到回复!"); Ok ( ( ) )} $cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 在 3.05 秒内完成 dev [unoptimized + debuginfo] target(s) Running `target/debug/waytoodeep`Jul 25 17: 12:32.276 INFO waytoodeep:您好,来自我们为自己制作的舒适巢穴7 月 25 日 17:12:32.409 INFO waytoodeep:收到回复! url=https://fasterthanli.me content_type=Some("text/html; charset=utf-8") 这就是我所说的结构化日志的意思。好吧,无论如何都是其中的一部分。在该行中:

我们有一条消息 Got a response!,然后是一个名为 url 的标记,其值为名为 url 的绑定的 Display-formatting,以及名为 content_type 的标记,其值为表达式 res.headers().get().get( “内容类型”)。十分简单! name = %value 用于 Display,name = ?value,用于 Debug,并且如果 name 和 value 具有相同的...名称,我们可以使用缩写形式 %value 和 ?value。当然还有跨度,它们很棒,对我来说,重点是你可以将它们发送到 APM 平台,如 Datadog 或 Honeycomb 或其他任何人,但这不是一篇关于跟踪的文章。只是为了说明,如果我们安装一个 JSON 跟踪订阅者,这就是我们得到的: $cargo run Compiling waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 在 3.09 中完成了 dev [unoptimized + debuginfo] 目标s 运行 `target/debug/waytoodeep`{"timestamp":"Jul 25 17:17:21.531","level":"INFO","fields":{"message":"Hello from a comfy nest we've为我们自己制作"},"target":"waytoodeep"}{"timestamp":"Jul 25 17:17:21.709","level":"INFO","fields":{"message":"得到回复!","url":"https://fasterthanli.me","content_type":"Some(\"text/html; charset=utf-8\")"},"target":"waytoodeep"} 。 ..所以这是一个公平的比较。这两篇文章都托管在我自己的网站上,这绝对不是营销计划,而是为了使获取时间具有可比性,并且有可能一个人先完成另一个获取(并且会随时间随机变化)。 async fn fetch_thing ( client: & Client, url: & str ) -> Result < ( ), Report > { let res = client .获取(网址)。发送 ( ) .await? . error_for_status ()? ;信息! (%url, content_type = ?res.headers ().get ("content-type"), "得到回复!");行 ( ( ) )}

# [tokio ::main ]async fn main ( ) -> 结果 < ( ), Report > { setup ( )? ;信息! (“你好,来自我们为自己做的舒适的巢穴”);让客户=客户::新(); fetch_thing ( &client, URL_1 ) ; fetch_thing ( &client, URL_2 ) ; Ok ( ( ) )} $cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep)warning: 未使用的`Future`实现者必须使用 --> src/main.rs:15:5 |15 | fetch_thing(&client, URL_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = 注意:`#[warn(unused_must_use)]` 默认开启 = 注意:期货什么都不做,除非你使用 `.await` 或轮询它们警告:必须使用的 `Future` 的未使用实现者 --> src/main.rs: 16:5 |16 | fetch_thing(&client, URL_2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = 注意:期货什么都不做,除非你`.await` 或轮询他们警告:发出 2 个警告 在 3.01 秒内完成开发 [未优化 + 调试信息] 目标运行 `target/debug/waytoodeep`Jul 25 17:26:31.571 INFO waytoodeep :你好,来自我们为自己制作的舒适的巢穴,长长的叹息 amos ffs 你忽略了黄色的波浪线和非常嘈杂的关于那些期货的 Rustwarnings 没有被调查只是为了证明一个观点,我明白了,我明白了,现在去修理它。 $cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 在 3.17 秒内完成 dev [unoptimized + debuginfo] 目标运行 `target/debug/waytoodeep`Jul 25 17:27:29.768 INFO waytoodeep:您好,来自我们为自己制作的舒适巢穴 7 月 25 日 17:27:29.891 信息 waytoodeep:收到回复! url=https://fasterthanli.me/articles/whats-in-the-box content_type=Some("text/html; charset=utf-8")Jul 25 17:27:29.974 INFO waytoodeep:得到回复! url=https://fasterthanli.me/series/advent-of-code-2020/part-13 content_type=Some("text/html; charset=utf-8") // 在 `src/dumb.rs` 中使用std ::{ future ::Future, pin ::Pin, task ::{Context, Poll},} ;使用追踪::信息; pub struct DumbFuture {} impl Future for DumbFuture { type Output = ( ) ; fn poll ( self: Pin < & mut Self >, _cx: & mut Context < ' _ > ) -> Poll <Self :: Output > { info ! (“你好来自愚蠢的未来!”); Poll ::Ready ( ( ) ) }} // 回到`src/main.rs` # [tokio ::main ]async fn main ( ) -> Result < ( ), Report > { setup ( )? ;让 fut = 哑 :: DumbFuture {} ; Ok ( ( ) )} $cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep)warning: 未使用的变量:`fut` --> src/main.rs:14:9 |14 | let fut = 笨蛋::DumbFuture {}; | ^^^ 帮助:如果这是故意的,请在其前面加上下划线:`_fut` | = 注意:`#[warn(unused_variables)]` 默认开启警告:发出 1 个警告 2.11 秒内完成的开发 [未优化 + 调试信息] 目标正在运行 `target/debug/waytoodeep`

如果我们 .await 它... 那么我们要求运行时运行它的事件循环直到轮询未来并且它最终返回 Poll::Ready,我们的立即执行:# [tokio ::main ]async fn main ( ) -> 结果 < ( ), Report > { setup ( )? ;信息! (“构建那个愚蠢的未来……”);让 fut = 哑 :: DumbFuture {} ;信息! (“等待那个愚蠢的未来……”);未来 .await ;信息! (“完成等待那个愚蠢的未来”); Ok ( ( ) )} $cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 在 2.34 秒内完成 dev [unoptimized + debuginfo] target(s) Running `target/debug/waytoodeep`Jul 25 17: 37:09.261 INFO waytoodeep:建立那个愚蠢的未来...... 7 月 25 日 17:37:09.261 INFO waytoodeep:等待那个愚蠢的未来...... 7 月 25 日 17:37:09.261 INFO waytoodeep::dumb:来自愚蠢的未来你好! 25 17:37:09.262 INFO waytoodeep:完成等待那个愚蠢的未来 这与 ECMAScript 承诺有点不同,它可以做一些工作,即使他们根本没有等待。但是不,Rust 期货只是无聊的状态机,如果你故意制造麻烦,你可以看到机器: // 在 `src/dumb.rs` impl Future for DumbFuture { type Output = ( ) ; fn poll ( self: Pin < & mut Self >, _cx: & mut Context < ' _ > ) -> Poll <Self :: Output > { panic ! (“哦不”); }} $ RUST_BACKTRACE=1 cargo run 编译waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 2.28s 内完成 dev [unoptimized + debuginfo] target(s) Running `target/debug/waytoodeep`Jul 25 17:41 :18.956 INFO waytoodeep: 构建那个愚蠢的未来... 7 月 25 日 17:41:18.956 INFO waytoodeep: 等待那个愚蠢的未来...应用程序惊慌失措(崩溃)。消息:哦,哎呀 noLocation: src/dumb.rs:14 ━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━ ⋮隐藏6帧⋮ 7: <waytoodeep::dumb::DumbFuture as core::future::future::Future>::poll: :h4a44780628f4c5f0 在 /home/amos/ftl/waytoodeep/src/dumb.rs:14 8:waytoodeep::main::{{closure}}::h36de5a1f1f2a5c5b 在 /home/amos/ftl/waytoodeep/src/main.rs :17 9: <core::future::from_generator::GenFuture<T> 作为 core::future::future::Future>::poll::h20a96e082c7a581e 在 /home/amos/.rustup/toolchains/stable-x86_64 -unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/mod.rs:80 10: tokio::park::thread::CachedParkThread::block_on::{{关闭}}::hdf98cb3c7fdf3de4 在/home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/park/thread.rs:263 11:tokio::coop::with_budget: :{{closure}}::h6a86a24a246e220f 在 /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/coop.rs:106 12: std::thread::local ::LocalKey<T>::try_with::h2ce0ac27c85965b6 在 /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local .rs:376 13: std::thread::local::LocalKey<T>::with::hc449f38c9f65fb53 在 /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/ src/rust/library/std/src/thread/local.rs:352 14: tokio::coop::with_budget::h5db157bd1e95e0e8 at /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio- 1.9.0/src/coop.rs:99 15:tokio::coop::budget::h7b57383f1255ac24 在/home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/ coop.rs:76 16: tokio::park::thread::CachedParkThread::block_on::hece399485213b91c 在 /home/amos /.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/park/thread.rs:263 17: tokio::runtime::enter::Enter::block_on::h89e9882e539e82d3 at /home /amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/runtime/enter.rs:151 18: tokio::runtime::thread_pool::ThreadPool::block_on::h1a0186470c00ba70 at /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/runtime/thread_pool/mod.rs:71 19: tokio::runtime::Runtime::block_on::h7c21d6989b86d606在 /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/runtime/mod.rs:452 20:waytoodeep::main::hb4dd5ffd46a5c032 在 /home/amos/ftl /waytoodeep/src/main.rs:20 21: core::ops::function::FnOnce::call_once::hc1fcc87431f77d25 at /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib /rustlib/src/rust/library/core/src/ops/function.rs:227 ⋮ 11 帧隐藏 ⋮ 使用 COLORBT_SHOW_HIDDEN=1 环境变量运行以禁用帧过滤。使用 RUST_BACKTRACE=full 运行以包含源代码片段宠物。

这对颜色更好,所以我希望你在家里跟着,但我们可以在第 20 帧看到我们实际的主函数,然后向上,我们可以看到 Runtime::block_on,一个线程池,一些停放的线程,线程-localstuff(另一个 TLS),一个生成的 future(第 9 帧和第 8 帧,基本上就是我们的 async fn main 最终的结果),最后是我们的 DumbFuture 的 pollmethod(第 7 帧)。但是请站出来,亲爱的观众,并用你的手臂绕过这个装置,以确保没有诡计,没有隐藏的电线,不……我要说的是异步堆栈跟踪没有“特殊处理”。在这里,当然,我们很恐慌,这只是 Rust 的事情,操作系统甚至从来不知道几乎避免了一场灾难。 DumbFuture 的 impl Future { type Output = ( ) ; fn poll ( self: Pin < & mut Self >, _cx: & mut Context < ' _ > ) -> Poll <Self :: Output > { unsafe { * ( 0xF00D as * mut u64 ) = 0x0 ;无法到达! ( ) ; // 小指承诺 }} $ RUST_BACKTRACE=1 cargo run 编译 waytoodeep v0.1.0 (/home/amos/ftl/waytoodeep) 在 2.18 秒内完成 dev [unoptimized + debuginfo] target(s) Running `target/debug/waytoodeep`Jul 25 17:46:53.926 INFO waytoodeep:构建那个愚蠢的未来……7 月 25 日 17:46:53.926 INFO waytoodeep:等待那个愚蠢的未来……zsh:分段错误(核心转储) RUST_BACKTRACE=1 货物运行 $cargo build && gdb --quiet --args ./target/debug/waytoodeep 在 0.04s 中完成 dev [unoptimized + debuginfo] 目标从 ./target/debug/waytoodeep 读取符号...警告:在偏移量 0 处缺少自动加载脚本在 .debug_gdb_scriptsof 文件 /home/amos/ftl/waytoodeep/target/debug/waytoodeep 部分中。使用 `info auto-load python-scripts [REGEXP]' 列出它们。(gdb) rStarting program: /home/amos/ftl/ waytoodeep/target/debug/waytoodeep [启用使用 libthread_db 的线程调试]使用主机 libthread_db 库“/lib/x86_64-linux-gnu/libthread_db.so.1”。[新线程 0x7ffff7c28700 (LWP 129418)][新线程 0x7f fff7a27700(LWP 129419)] [新主题0x7ffff7826700(LWP 129420)] [新主题0x7ffff7625700(LWP 129421)] [新主题0x7ffff7424700(LWP 129422)] [新主题0x7ffff7223700(LWP 129423)] [新主题0x7ffff7022700(LWP 129424) ] [新主题0x7ffff6e1e700(LWP 129425)] [新主题0x7ffff6c1a700(LWP 129426)] [新主题0x7ffff6a16700(LWP 129427)] [新主题0x7ffff6812700(LWP 129428)] [新主题0x7ffff660e700(LWP 129429)] [新主题0x7ffff640a700 (LWP 129430)][新线程 0x7ffff6206700 (LWP 129431)][新线程 0x7ffff6002700 (LWP 129432)]7 月 25 日 17:47:13.278 INFO waytoodeep...J17todup:J17todeep57137 构建那个等待那个愚蠢的未来...线程 1“waytoodeep”收到信号 SIGSEGV,分段错误。<waytoodeep::dumb::DumbFuture as core::future::future::Future>::poll (self=..., _cx =0x7fffffffd690) at src/dumb.rs:1515 *(0xF00D as *mut u64) = 0x0;(gdb) bt#0 <waytoodeep::dumb::DumbFuture as core::future::future::Future>::民意调查 (self=..., _cx=0x7fffffffd690) a t src/dumb.rs:15#1 0x0000555555ab3a3 in waytoodeep::main::{{closure}} () at src/main.rs:17#2 0x00005555555adb29 in <core::future::from_generator::GenFuture<T > 作为 core::future::future::Future>::poll (self=..., cx=0x7fffffffd690) 在 /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/ rustlib/src/rust/library/core/src/future/mod.rs:80#3 0x00005555555adaa0 in tokio::park::thread::CachedParkThread::block_on::{{closure}} () at /home/amos /.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/park/thread.rs:263#4 0x00005555555b1742 in tokio::coop::with_budget::{{closure}}(单元格= 0x7ffff7c2c412) 在 /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/coop.rs:106#5 0x00005555555a9f58 在 std::<thread::Tlocal 中>::try_with (self=0x555555925fc0, f=...) 在 /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/线程/local.rs:376#6 0x00005555555a9e3d in std::thread::local::LocalKey<T>::with (self=0x55555 5925fc0, f=...) 在 /home/amos/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:352 #7 0x00005555555ad7c8 in tokio::coop::with_budget (budget=..., f=...) at /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src /coop.rs:99#8 tokio::coop::budget (f=...) 在 /home/amos/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.9.0/src/coop .rs:76#9 tokio::park::thread::CachedParkThread::block_on (self=0x7fffff ......