diff --git a/po/ko.po b/po/ko.po index fd87a18a..197618dd 100644 --- a/po/ko.po +++ b/po/ko.po @@ -12523,6 +12523,1475 @@ msgstr "" "}\n" "```" +#: src/async.md:3 +msgid "" +"\"Async\" is a concurrency model where multiple tasks are executed " +"concurrently by\n" +"executing each task until it would block, then switching to another task " +"that is\n" +"ready to make progress. The model allows running a larger number of tasks on " +"a\n" +"limited number of threads. This is because the per-task overhead is " +"typically\n" +"very low and operating systems provide primitives for efficiently " +"identifying\n" +"I/O that is able to proceed." +msgstr "" +"\"Async\"는 블럭될(더 이상 진행할 수 없을) 때까지 각 작업을 실행한 다음\n" +"진행할 준비가 된 다른 작업으로 전환하여\n" +"여러 작업을 동시에 실행하는 동시 실행 모델입니다. 이 모델을 사용하면 제한된 " +"수의 스레드에서 더 많은 작업을\n" +"실행할 수 있습니다. 이는, 한 작업을 유지하고 수행하는데 필요한 오버헤드가 (스레드에 비해) 매우 낮고\n" +"운영체제가 여러 I/O들에서 현재 진행 가능한 I/O들을 효과적으로 식별해 주는 프리미티브를\n" +"제공하기 때문입니다." + +#: src/async.md:10 +msgid "" +"Rust's asynchronous operation is based on \"futures\", which represent work " +"that\n" +"may be completed in the future. Futures are \"polled\" until they signal " +"that\n" +"they are complete." +msgstr "" +"Rust의 비동기 작업은 \"futures\"를 기반으로 하며 이는 미래에 완료될 수 있는\n" +"작업을 나타냅니다. Futures는 완료되었다는 신호를 보낼 때까지\n" +"\"폴링\"됩니다." + +#: src/async.md:14 +msgid "" +"Futures are polled by an async runtime, and several different runtimes are\n" +"available." +msgstr "" +"Futures는 비동기 런타임에 의해 폴링되며, 비동기 런타임에는 여러 다양한 종류가\n" +"있습니다." + +#: src/async.md:19 +msgid "" +" * Python has a similar model in its `asyncio`. However, its `Future` type " +"is\n" +" callback-based, and not polled. Async Python programs require a " +"\"loop\",\n" +" similar to a runtime in Rust.\n" +"\n" +" * JavaScript's `Promise` is similar, but again callback-based. The " +"language\n" +" runtime implements the event loop, so many of the details of Promise\n" +" resolution are hidden." +msgstr "" +" * 파이썬에도 `asyncio`라는 유사한 모델이 있습니다. 그러나 파이썬의 `Future` 타입은\n" +"콜백 기반이며 폴링되지 않습니다. 파이썬으로 비동기 프로그래밍을 할 때에는, Rust에서 런타임이 " +"내부적으로 해 주는 것과 유사한, \n" +"\"루프\"를 명시적으로 사용해야 합니다.\n" +"\n" +" * 자바스크립트의 `Promise`도 비슷하지만 역시 콜백 기반입니다. 자바스크립트에서는 이벤트 루프가" +"런타임 엔진에서 구현되므로\n" +"`Promise`가 처리되는 세부 과정이\n" +"숨겨집니다." + +#: src/async/async-await.md:1 +msgid "# `async`/`await`" +msgstr "# `async`/`await`" + +#: src/async/async-await.md:3 +msgid "" +"At a high level, async Rust code looks very much like \"normal\" sequential " +"code:" +msgstr "겉에서 보았을 때, 비동기 Rust 코드는 일반적인 절차적 코드와 매우 유사합니다." + +#: src/async/async-await.md:5 +msgid "" +"```rust,editable,compile_fail\n" +"use futures::executor::block_on;\n" +"\n" +"async fn count_to(count: i32) {\n" +" for i in 1..=count {\n" +" println!(\"Count is: {i}!\");\n" +" }\n" +"}\n" +"\n" +"async fn async_main(count: i32) {\n" +" count_to(count).await;\n" +"}\n" +"\n" +"fn main() {\n" +" block_on(async_main(10));\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use futures::executor::block_on;\n" +"\n" +"async fn count_to(count: i32) {\n" +" for i in 1..=count {\n" +" println!(\"Count is: {i}!\");\n" +" }\n" +"}\n" +"\n" +"async fn async_main(count: i32) {\n" +" count_to(count).await;\n" +"}\n" +"\n" +"fn main() {\n" +" block_on(async_main(10));\n" +"}\n" +"```" + +#: src/async/async-await.md:27 +msgid "" +"* Note that this is a simplified example to show the syntax. There is no " +"long\n" +" running operation or any real concurrency in it!\n" +"\n" +"* What is the return type of an async call?\n" +" * Use `let future: () = async_main(10);` in `main` to see the type.\n" +"\n" +"* The \"async\" keyword is syntactic sugar. The compiler replaces the return " +"type\n" +" with a future. \n" +"\n" +"* You cannot make `main` async, without additional instructions to the " +"compiler\n" +" on how to use the returned future.\n" +"\n" +"* You need an executor to run async code. `block_on` blocks the current " +"thread\n" +" until the provided future has run to completion. \n" +"\n" +"* `.await` asynchronously waits for the completion of another operation. " +"Unlike\n" +" `block_on`, `.await` doesn't block the current thread.\n" +"\n" +"* `.await` can only be used inside an `async` function (or block; these are\n" +" introduced later). " +msgstr "" +"* Rust 비동기 문법을 보여주는 간단한 예시입니다. 여기에는 오래 실행되는 작업이나, 실제로 동시에\n" +" 수행되는 것들은 없습니다.\n" +"\n" +"* `async`함수의 리턴 타입은 무엇인가요?\n" +" * `main`에서 `let future: () = async_main(10);을 사용하여 타입을 확인하세" +"요.\n" +"\n" +"* \"async\" 키워드는 문법 설탕(syntactic sugar)입니다. 컴파일러가 리턴 타입을 future로\n" +" 바꿉니다. \n" +"\n" +"* `main`을 비동기 함수로 만들수는 없습니다. 만약 그렇게 할 경우 컴파일러는 리턴 타입인 future를\n" +" 어떻게 사용할 지 모르기 때문입니다.\n" +"\n" +"* 비동기 코드를 실행하려면 실행자(executor)가 필요합니다. `block_on` 실행자는 제공된 future" +"가\n" +" 완료될 때까지 현재 스레드를 블록합니다. \n" +"\n" +"* `.await`는 다른 작업이 완료될 때까지 비동기적으로 대기합니다. `block_on`과 " +"달리\n" +" `.await`는 현재 스레드를 블록하지 않습니다.\n" +"\n" +"* `.await`는 `async` 함수(또는 나중에 소개될 `async` 블록) 안에서만\n" +" 사용할 수 있습니다. " + +#: src/async/futures.md:3 +msgid "" +"[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html)\n" +"is a trait, implemented by objects that represent an operation that may not " +"be\n" +"complete yet. A future can be polled, and `poll` returns a\n" +"[`Poll`](https://doc.rust-lang.org/std/task/enum.Poll.html)." +msgstr "" +"[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html)는 트레잇입니다." +"이 트레잇은 아직\n" +"완료되지 않았을 수도 있는 작업을 나타냅니다.\n" +"Future는 `poll` 함수를 통해 폴링될 수 있으며, 이 함수는\n" +"[`Poll`](https://doc.rust-lang.org/std/task/enum.Poll.html)을 반환합니다." + +#: src/async/futures.md:8 +msgid "" +"```rust\n" +"use std::pin::Pin;\n" +"use std::task::Context;\n" +"\n" +"pub trait Future {\n" +" type Output;\n" +" fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll;\n" +"}\n" +"\n" +"pub enum Poll {\n" +" Ready(T),\n" +" Pending,\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"use std::pin::Pin;\n" +"use std::task::Context;\n" +"\n" +"pub trait Future {\n" +" type Output;\n" +" fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll;\n" +"}\n" +"\n" +"pub enum Poll {\n" +" Ready(T),\n" +" Pending,\n" +"}\n" +"```" + +#: src/async/futures.md:23 +msgid "" +"An async function returns an `impl Future`. It's also possible (but " +"uncommon) to\n" +"implement `Future` for your own types. For example, the `JoinHandle` " +"returned\n" +"from `tokio::spawn` implements `Future` to allow joining to it." +msgstr "" +"비동기 함수는 `impl Future`를 반환합니다. 새로운 타입을 만들고 이 타입이 `Future`를 구현하게 할 수도 " +"있지만\n" +"일반적이지는 않습니다. 예를 들어 `tokio::spawn`가 리턴하는 `JoinHandle`은\n" +"`Future`를 구현하며, 이를 통해 생성된 스레드에 join할 수 있습니다." + +#: src/async/futures.md:27 +msgid "" +"The `.await` keyword, applied to a Future, causes the current async function " +"to\n" +"pause until that Future is ready, and then evaluates to its output." +msgstr "" +"Future에 `.await`를 호출하면, 해당 Future가 준비될 때까지 현재 비동기 함" +"수가 일시 중지됩니다. 그런 다음 Future가 준비가 되면, 그 값이 `.await` 구문의 값이 됩니다." + +#: src/async/futures.md:32 +msgid "" +"* The `Future` and `Poll` types are implemented exactly as shown; click the\n" +" links to show the implementations in the docs.\n" +"\n" +"* We will not get to `Pin` and `Context`, as we will focus on writing async\n" +" code, rather than building new async primitives. Briefly:\n" +"\n" +" * `Context` allows a Future to schedule itself to be polled again when an\n" +" event occurs.\n" +"\n" +" * `Pin` ensures that the Future isn't moved in memory, so that pointers " +"into\n" +" that future remain valid. This is required to allow references to " +"remain\n" +" valid after an `.await`." +msgstr "" +"* `Future` 와 `Poll` 타입의 실제 정의는 위에 보이는 그대로 입니다. 링크를 클릭하면\n" +" Rust 문서에서 한 번 더 확인할 수 있습니다.\n" +"\n" +"* 본 강의의 목적은 비동기 코드를 작성하는데 있기 때문에, 새로운 비동기 프리미티브를 만드는데 필요한 \n" +" `Pin`과 `Context`는 다루지 않습니다. 이들에 대해 간단히 설명하자면:\n" +"\n" +" * `Context`를 사용하면 Future가 이벤트가 발생할 때 다시 폴링되도록\n" +" 예약할 수 있습니다.\n" +"\n" +" * 'Pin'을 사용하면 메모리에서 Future의 위치가 고정되기 때문에 해당 future의 " +"포인터가\n" +" 항상 유효하게 유지됩니다. 이는 `.await` 후에 참조를 유효한 상태로 유지하기 위" +"해\n" +" 필요합니다." + +#: src/async/runtimes.md:3 +msgid "" +"A *runtime* provides support for performing operations asynchronously (a\n" +"*reactor*) and is responsible for executing futures (an *executor*). Rust " +"does not have a\n" +"\"built-in\" runtime, but several options are available:" +msgstr "" +"비동기 *런타임*은 *리액터* (비동기식 작업 실행을 지원)와 *실행자* (futures를 실행)" +"의 두 가지 역할을 합니다. Rust 언어 자체에서 기본 제공하는 비동기 런타임은 없습니다. 그러나 다음과 같은\n" +"비동기 런타임 크레잇들이 있습니다." + +#: src/async/runtimes.md:7 +msgid "" +" * [Tokio](https://tokio.rs/) - performant, with a well-developed ecosystem " +"of\n" +" functionality like [Hyper](https://hyper.rs/) for HTTP or\n" +" [Tonic](https://github.com/hyperium/tonic) for gRPC.\n" +" * [async-std](https://async.rs/) - aims to be a \"std for async\", and " +"includes a\n" +" basic runtime in `async::task`.\n" +" * [smol](https://docs.rs/smol/latest/smol/) - simple and lightweight" +msgstr "" +" * [Tokio](https://tokio.rs/) - 성능이 우수합니다. 그리고 HTTP를 지원하는 [Hyper](https://hyper." +"rs/) 와\n" +" gRPC를 지원하는 [Tonic](https://github.com/hyperium/tonic)과 같은 잘 발달된\n" +" 라이브러리 생태계가 있습니다.\n" +" * [async-std](https://async.rs/) - 비동기에서의 `std`를 목표로 하고 있습니다. `async::" +"task`에 기본 런타임이\n" +"포함되어 있습니다.\n" +" * [smol](https://docs.rs/smol/latest/smol/) - 간단하고 가볍습니다." + +#: src/async/runtimes.md:14 +msgid "" +"Several larger applications have their own runtimes. For example,\n" +"[Fuchsia](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/lib/" +"fuchsia-async/src/lib.rs)\n" +"already has one." +msgstr "" +"여러 대규모 애플리케이션에는 자체 런타임이 있는 경우도 있습니다. 예들 들어 \n" +"[Fuchsia](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/lib/" +"fuchsia-async/src/lib.rs)가 있습니다." + +#: src/async/runtimes.md:20 +msgid "" +"* Note that of the listed runtimes, only Tokio is supported in the Rust\n" +" playground. The playground also does not permit any I/O, so most " +"interesting\n" +" async things can't run in the playground.\n" +"\n" +"* Futures are \"inert\" in that they do not do anything (not even start an I/" +"O\n" +" operation) unless there is an executor polling them. This differs from JS\n" +" Promises, for example, which will run to completion even if they are " +"never\n" +" used." +msgstr "" +"* Rust 플레이그라운드에서는 위에 나열된 비동기 런타임 중에서 Tokio만\n" +" 사용할 수 있습니다. 또한 Rust 플레이그라운드는 I/O를 허용하지 않으므로 async를 가지고 할 수 있는\n" +" 많은 흥미로운 작업들이 불가능 합니다.\n" +"\n" +"* Futures는 실행자가 폴링하지 않는 한 아무것도 하지 않는다는 점에서(I/O 작업" +"조차 시작하지 않음)\n" +" \"비활성\" 상태입니다. 이는 사용되지 않는 경우에도\n" +" 완료될 때 까지 실행되는, 자바 스크립트의 promise와\n" +" 다릅니다." + +#: src/async/runtimes/tokio.md:1 +msgid "# Tokio" +msgstr "# Tokio" + +#: src/async/runtimes/tokio.md:4 +msgid "Tokio provides: " +msgstr "Tokio는 다음을 제공합니다. " + +#: src/async/runtimes/tokio.md:6 +msgid "" +"* A multi-threaded runtime for executing asynchronous code.\n" +"* An asynchronous version of the standard library.\n" +"* A large ecosystem of libraries." +msgstr "" +"* 비동기 코드 실행을 위한 멀티스레드 런타임\n" +"* 표준 라이브러리의 비동기 버전\n" +"* 대규모 라이브러리 생태계" + +#: src/async/runtimes/tokio.md:10 +msgid "" +"```rust,editable,compile_fail\n" +"use tokio::time;\n" +"\n" +"async fn count_to(count: i32) {\n" +" for i in 1..=count {\n" +" println!(\"Count in task: {i}!\");\n" +" time::sleep(time::Duration::from_millis(5)).await;\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" tokio::spawn(count_to(10));\n" +"\n" +" for i in 1..5 {\n" +" println!(\"Main task: {i}\");\n" +" time::sleep(time::Duration::from_millis(5)).await;\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use tokio::time;\n" +"\n" +"async fn count_to(count: i32) {\n" +" for i in 1..=count {\n" +" println!(\"Count in task: {i}!\");\n" +" time::sleep(time::Duration::from_millis(5)).await;\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" tokio::spawn(count_to(10));\n" +"\n" +" for i in 1..5 {\n" +" println!(\"Main task: {i}\");\n" +" time::sleep(time::Duration::from_millis(5)).await;\n" +" }\n" +"}\n" +"```" + +#: src/async/runtimes/tokio.md:33 +msgid "" +"* With the `tokio::main` macro we can now make `main` async.\n" +"\n" +"* The `spawn` function creates a new, concurrent \"task\".\n" +"\n" +"* Note: `spawn` takes a `Future`, you don't call `.await` on `count_to`." +msgstr "" +"* 이제 `tokio::main` 매크로를 사용하면 `main`을 비동기로 만들 수 있습니다.\n" +"\n" +"* `spawn` 함수는 동시 실행되는 새로운 \"작업\"을 만듭니다.\n" +"\n" +"* 참고: `spawn`은 `Future`를 인자로 받습니다. 때문에 `count_to`에 `.await`를 호출하지 " +"않는 점을 주목하세요." + +#: src/async/runtimes/tokio.md:39 +msgid "**Further exploration:**" +msgstr "**심화 학습:**" + +#: src/async/runtimes/tokio.md:41 +msgid "" +"* Why does `count_to` not (usually) get to 10? This is an example of async\n" +" cancellation. `tokio::spawn` returns a handle which can be awaited to " +"wait\n" +" until it finishes.\n" +"\n" +"* Try `count_to(10).await` instead of spawning.\n" +"\n" +"* Try awaiting the task returned from `tokio::spawn`." +msgstr "" +"* `count_to`가 10에 도달하지 않는 경우가 많은데 그 이유는 무엇일까요? 이는 비동" +"기적인 취소를\n" +" 보여주는 예입니다. `tokio::spawn`이 리턴하는 것은 완료될 때까지 기다리도록 대기하는데 사용되는 핸들입니다.\n" +"\n" +"* `tokio::spawn` 대신 `count_to(10).await`를 사용해 보세요.\n" +"\n" +"* `tokio::spawn`에서 반환된 작업을 `await` 해 보세요." + +#: src/async/tasks.md:3 +msgid "Rust has a task system, which is a form of lightweight threading." +msgstr "Rust의 태스크(작업) 시스템은 경량 스레딩의 한 종류로 볼 수 있습니다." + +#: src/async/tasks.md:5 +msgid "" +"A task has a single top-level future which the executor polls to make " +"progress.\n" +"That future may have one or more nested futures that its `poll` method " +"polls,\n" +"corresponding loosely to a call stack. Concurrency within a task is possible " +"by\n" +"polling multiple child futures, such as racing a timer and an I/O operation." +msgstr "" +"하나의 작업에는, 실행자가 이 작업을 진행하기 위해 계속 폴링하는, 최상위 future가 한 개 있습니다.\n" +"이 future에는 `poll` 메서드가 폴링하는 중첩된 future가 한 개 이상 있을 수 있습" +"니다.\n" +"이러한 중첩된 future는 일반적인 함수 호출 스택하고 비슷한 역할을 합니다. 한 작업 안에서 여러 자식 future들을 " +"폴링하면, 타이머를 켜는 것과 어떤 I/O작업을 동시에 수행시킨 후 타이머와 I/O 중 먼저 끝나는 것을 기다리는 것과 같은" +"동시성도 구현할 수 있습니다." + +#: src/async/tasks.md:10 +msgid "" +"```rust,compile_fail\n" +"use tokio::io::{self, AsyncReadExt, AsyncWriteExt};\n" +"use tokio::net::TcpListener;\n" +"\n" +"#[tokio::main]\n" +"async fn main() -> io::Result<()> {\n" +" let listener = TcpListener::bind(\"127.0.0.1:6142\").await?;\n" +"\tprintln!(\"listening on port 6142\");\n" +"\n" +" loop {\n" +" let (mut socket, addr) = listener.accept().await?;\n" +"\n" +" println!(\"connection from {addr:?}\");\n" +"\n" +" tokio::spawn(async move {\n" +" if let Err(e) = socket.write_all(b\"Who are you?\\n\").await {\n" +" println!(\"socket error: {e:?}\");\n" +" return;\n" +" }\n" +"\n" +" let mut buf = vec![0; 1024];\n" +" let reply = match socket.read(&mut buf).await {\n" +" Ok(n) => {\n" +" let name = std::str::from_utf8(&buf[..n]).unwrap()." +"trim();\n" +" format!(\"Thanks for dialing in, {name}!\\n\")\n" +" }\n" +" Err(e) => {\n" +" println!(\"socket error: {e:?}\");\n" +" return;\n" +" }\n" +" };\n" +"\n" +" if let Err(e) = socket.write_all(reply.as_bytes()).await {\n" +" println!(\"socket error: {e:?}\");\n" +" }\n" +" });\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust,compile_fail\n" +"use tokio::io::{self, AsyncReadExt, AsyncWriteExt};\n" +"use tokio::net::TcpListener;\n" +"\n" +"#[tokio::main]\n" +"async fn main() -> io::Result<()> {\n" +" let listener = TcpListener::bind(\"127.0.0.1:6142\").await?;\n" +"\tprintln!(\"listening on port 6142\");\n" +"\n" +" loop {\n" +" let (mut socket, addr) = listener.accept().await?;\n" +"\n" +" println!(\"connection from {addr:?}\");\n" +"\n" +" tokio::spawn(async move {\n" +" if let Err(e) = socket.write_all(b\"Who are you?\\n\").await {\n" +" println!(\"socket error: {e:?}\");\n" +" return;\n" +" }\n" +"\n" +" let mut buf = vec![0; 1024];\n" +" let reply = match socket.read(&mut buf).await {\n" +" Ok(n) => {\n" +" let name = std::str::from_utf8(&buf[..n]).unwrap()." +"trim();\n" +" format!(\"Thanks for dialing in, {name}!\\n\")\n" +" }\n" +" Err(e) => {\n" +" println!(\"socket error: {e:?}\");\n" +" return;\n" +" }\n" +" };\n" +"\n" +" if let Err(e) = socket.write_all(reply.as_bytes()).await {\n" +" println!(\"socket error: {e:?}\");\n" +" }\n" +" });\n" +" }\n" +"}\n" +"```" + +#: src/async/tasks.md:52 src/async/control-flow/join.md:36 +msgid "" +"Copy this example into your prepared `src/main.rs` and run it from there." +msgstr "이 예제를, 로컬 컴퓨터에 만들어 둔 `src/main.rs`에 복사하고 거기에서 실행하세요." + +#: src/async/tasks.md:54 +msgid "" +"* Ask students to visualize what the state of the example server would be " +"with a\n" +" few connected clients. What tasks exist? What are their Futures?\n" +"\n" +"* This is the first time we've seen an `async` block. This is similar to a\n" +" closure, but does not take any arguments. Its return value is a Future,\n" +" similar to an `async fn`. \n" +"\n" +"* Refactor the async block into a function, and improve the error handling " +"using `?`." +msgstr "" +"* 수강생들에게 이 서버에 몇 개의 클라이언트가 연결되면 이 서버의 상태가 어떻게 변할지 그림을 그려보도록 하세요.\n" +" 어떤 태스크들이 있는지, 이 태스크들의 Future는 어떤 상태에 있는지 물어봅니다.\n" +"\n" +"* `async` 블록을 처음 보게 되었습니다. 이것은 클로저와 비슷하지만\n" +" 인자를 받지 않습니다. 리턴 타입은 `async fn`과 비슷한\n" +" Future입니다. \n" +"\n" +"* Async 블록을 함수로 리팩터링하고 `?`를 사용하여 오류 처리를 개선해 봅시다." + +#: src/async/channels.md:3 +msgid "" +"Several crates have support for asynchronous channels. For instance `tokio`:" +msgstr "" +"여러 크레이트에서 비동기 채널을 지원합니다. 예를 들어 `tokio`에서는 아래와 같이" +"합니다." + +#: src/async/channels.md:5 +msgid "" +"```rust,editable,compile_fail\n" +"use tokio::sync::mpsc::{self, Receiver};\n" +"\n" +"async fn ping_handler(mut input: Receiver<()>) {\n" +" let mut count: usize = 0;\n" +"\n" +" while let Some(_) = input.recv().await {\n" +" count += 1;\n" +" println!(\"Received {count} pings so far.\");\n" +" }\n" +"\n" +" println!(\"ping_handler complete\");\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (sender, receiver) = mpsc::channel(32);\n" +" let ping_handler_task = tokio::spawn(ping_handler(receiver));\n" +" for i in 0..10 {\n" +" sender.send(()).await.expect(\"Failed to send ping.\");\n" +" println!(\"Sent {} pings so far.\", i + 1);\n" +" }\n" +"\n" +" std::mem::drop(sender);\n" +" ping_handler_task.await.expect(\"Something went wrong in ping handler " +"task.\");\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use tokio::sync::mpsc::{self, Receiver};\n" +"\n" +"async fn ping_handler(mut input: Receiver<()>) {\n" +" let mut count: usize = 0;\n" +"\n" +" while let Some(_) = input.recv().await {\n" +" count += 1;\n" +" println!(\"Received {count} pings so far.\");\n" +" }\n" +"\n" +" println!(\"ping_handler complete\");\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (sender, receiver) = mpsc::channel(32);\n" +" let ping_handler_task = tokio::spawn(ping_handler(receiver));\n" +" for i in 0..10 {\n" +" sender.send(()).await.expect(\"Failed to send ping.\");\n" +" println!(\"Sent {} pings so far.\", i + 1);\n" +" }\n" +"\n" +" std::mem::drop(sender);\n" +" ping_handler_task.await.expect(\"Something went wrong in ping handler " +"task.\");\n" +"}\n" +"```" + +#: src/async/channels.md:35 +msgid "" +"* Change the channel size to `3` and see how it affects the execution.\n" +"\n" +"* Overall, the interface is similar to the `sync` channels as seen in the\n" +" [morning class](concurrency/channels.md).\n" +"\n" +"* Try removing the `std::mem::drop` call. What happens? Why?\n" +"\n" +"* The [Flume](https://docs.rs/flume/latest/flume/) crate has channels that\n" +" implement both `sync` and `async` `send` and `recv`. This can be " +"convenient\n" +" for complex applications with both IO and heavy CPU processing tasks.\n" +"\n" +"* What makes working with `async` channels preferable is the ability to " +"combine\n" +" them with other `future`s to combine them and create complex control flow." +msgstr "" +"* 채널 크기를 `3`으로 변경하고 동작이 어떻게 바뀌는지 확인하세요.\n" +"\n" +"* 비동기 채널을 사용하기 위한 인터페이스는 [오전 과정](concurrency/channels.md)에서 배운\n" +" `sync` 채널과 비슷합니다.\n" +"\n" +"* `std::mem::drop` 호출하는 줄을 삭제해 보세요. 어떤 결과가 나타나나요? 이유가 무엇" +"인가요?\n" +"\n" +"* [Flume](https://docs.rs/flume/latest/flume/) 크레이트에는 `sync`와 " +"`async`, `send`와 `recv`를\n" +" 모두 구현하는 채널이 있습니다. 이것은 IO와 CPU 처리 작업이 많은\n" +" 복잡한 애플리케이션을 구현할 때 매우 유용합니다.\n" +"\n" +"* `async` 채널을 사용하는 것이 더 좋은 이유는 이를 다른 `future`와 결합하여\n" +" 복잡한 제어 흐름을 만들 수 있기 때문입니다." + +#: src/async/control-flow.md:3 +msgid "" +"Futures can be combined together to produce concurrent compute flow graphs. " +"We\n" +"have already seen tasks, that function as independent threads of execution." +msgstr "" +"Future들을 결합하여 계산 과정을 동시성이 있는 플로우 그래프 형태로 모델링 할 수 있습니다. 앞서 배운, \n" +"각 태스크가 독립적으로 수행되도록 하는 것도 Future들을 결합하는 한 방법으로 볼 수 있습니다." + +#: src/async/control-flow.md:6 +msgid "" +"- [Join](control-flow/join.md)\n" +"- [Select](control-flow/select.md)" +msgstr "" +"- [Join](control-flow/join.md)\n" +"- [Select](control-flow/select.md)" + +#: src/async/control-flow/join.md:1 +msgid "# Join" +msgstr "# Join" + +#: src/async/control-flow/join.md:3 +msgid "" +"A join operation waits until all of a set of futures are ready, and\n" +"returns a collection of their results. This is similar to `Promise.all` in\n" +"JavaScript or `asyncio.gather` in Python." +msgstr "" +"Join 연산은 모든 future가 준비될 때까지 기다린 후, 각 future의 결과값을 담은 컬렉션을\n" +"리턴합니다. 이는 자바스크립트의 `Promise.all`이나 파이썬의 `asyncio.gather`와\n" +"유사합니다." + +#: src/async/control-flow/join.md:7 +msgid "" +"```rust,editable,compile_fail\n" +"use anyhow::Result;\n" +"use futures::future;\n" +"use reqwest;\n" +"use std::collections::HashMap;\n" +"\n" +"async fn size_of_page(url: &str) -> Result {\n" +" let resp = reqwest::get(url).await?;\n" +" Ok(resp.text().await?.len())\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let urls: [&str; 4] = [\n" +" \"https://google.com\",\n" +" \"https://httpbin.org/ip\",\n" +" \"https://play.rust-lang.org/\",\n" +" \"BAD_URL\",\n" +" ];\n" +" let futures_iter = urls.into_iter().map(size_of_page);\n" +" let results = future::join_all(futures_iter).await;\n" +" let page_sizes_dict: HashMap<&str, Result> =\n" +" urls.into_iter().zip(results.into_iter()).collect();\n" +" println!(\"{:?}\", page_sizes_dict);\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use anyhow::Result;\n" +"use futures::future;\n" +"use reqwest;\n" +"use std::collections::HashMap;\n" +"\n" +"async fn size_of_page(url: &str) -> Result {\n" +" let resp = reqwest::get(url).await?;\n" +" Ok(resp.text().await?.len())\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let urls: [&str; 4] = [\n" +" \"https://google.com\",\n" +" \"https://httpbin.org/ip\",\n" +" \"https://play.rust-lang.org/\",\n" +" \"BAD_URL\",\n" +" ];\n" +" let futures_iter = urls.into_iter().map(size_of_page);\n" +" let results = future::join_all(futures_iter).await;\n" +" let page_sizes_dict: HashMap<&str, Result> =\n" +" urls.into_iter().zip(results.into_iter()).collect();\n" +" println!(\"{:?}\", page_sizes_dict);\n" +"}\n" +"```" + +#: src/async/control-flow/join.md:38 +msgid "" +"* For multiple futures of disjoint types, you can use `std::future::join!` " +"but\n" +" you must know how many futures you will have at compile time. This is\n" +" currently in the `futures` crate, soon to be stabilised in `std::future`.\n" +"\n" +"* The risk of `join` is that one of the futures may never resolve, this " +"would\n" +" cause your program to stall. \n" +"\n" +"* You can also combine `join_all` with `join!` for instance to join all " +"requests\n" +" to an http service as well as a database query. Try adding a\n" +" `tokio::time::sleep` to the future, using `futures::join!`. This is not a\n" +" timeout (that requires `select!`, explained in the next chapter), but " +"demonstrates `join!`." +msgstr "" +"* 서로 다른 타입을 가지는 여러 여러 futures들을 join하고자 할 경우 `std::future::join!`을 사용할 수 있습니다.\n" +" 이 매크로를 사용하려면 futures가 몇 개나 있을지 컴파일 할 때 알아야 한다는 점을 주의하세요. 이 매크로는 지금은\n" +" 'futures' 크레이트에 있으며 곧 안정화 되어 `std::future`에 포함될 예정입니다.\n" +"\n" +"* 'join'의 위험성은 futures들 중 하나가 영영 끝나지 않을 수도 있다는 것입니다. 그러" +"면\n" +" 프로그램이 더이상 진행을 못하고 멈춰있을(stall) 수 있습니다. \n" +"\n" +"* `join_all`을 `join!`과 결합하여 http 서비스와 데이터베이스에 대한 모든 요청들을 한꺼번에 진행시킬 수도 있습니다.\n" +" `futures::join!`을 사용하여\n" +" `tokio::time::sleep`을 future에 추가해 보세요. 이건 타임아웃을 구현하는 것이 아님을 주의하세요. \n" +" 실제로, 타임아웃은 다음 장에서 설명하는 `select!`를 사용해서 구현해야 합니다. " +" 여기서는 `tokio::time::sleep`을 사용한 것은 단순히 `join!`의 동작을 설명하기 위함입니다." + +#: src/async/control-flow/select.md:3 +msgid "" +"A select operation waits until any of a set of futures is ready, and " +"responds to\n" +"that future's result. In JavaScript, this is similar to `Promise.race`. In\n" +"Python, it compares to `asyncio.wait(task_set,\n" +"return_when=asyncio.FIRST_COMPLETED)`." +msgstr "" +"Select 연산은 여러 future들 모두에 대해서 준비될 때 까지 기다리다가, 그 중 어떤 한 future가 최초로 준비 상태가 되면 해당 future의 결과값을\n" +"리턴합니다. 이것은 자바스크립트에서의 `Promise.race`와 비슷합니다. 파이썬에서라면\n" +"`asyncio.wait(task_set,\n" +"return_when=asyncio.FIRST_COMPLETED)`가 하는 동작과 비슷합니다." + +#: src/async/control-flow/select.md:8 +msgid "" +"Similar to a match statement, the body of `select!` has a number of arms, " +"each\n" +"of the form `pattern = future => statement`. When the `future` is ready, " +"the\n" +"`statement` is executed with the variables in `pattern` bound to the " +"`future`'s\n" +"result." +msgstr "" +" `select!` 안에는, `match`문과 비슷하게, `pattern = future => " +"statement` 형태의 브랜치(arm)\n" +"들이 있습니다. 어떤 'future'가 진행 가능 상태가 되면\n" +"'그 `future`의 결과값이 `pattern`으로 바인딩 되며, 그 상태에서 `statement'가 수행됩니다." + +#: src/async/control-flow/select.md:13 +msgid "" +"```rust,editable,compile_fail\n" +"use tokio::sync::mpsc::{self, Receiver};\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"#[derive(Debug, PartialEq)]\n" +"enum Animal {\n" +" Cat { name: String },\n" +" Dog { name: String },\n" +"}\n" +"\n" +"async fn first_animal_to_finish_race(\n" +" mut cat_rcv: Receiver,\n" +" mut dog_rcv: Receiver,\n" +") -> Option {\n" +" tokio::select! {\n" +" cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),\n" +" dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (cat_sender, cat_receiver) = mpsc::channel(32);\n" +" let (dog_sender, dog_receiver) = mpsc::channel(32);\n" +" tokio::spawn(async move {\n" +" sleep(Duration::from_millis(500)).await;\n" +" cat_sender\n" +" .send(String::from(\"Felix\"))\n" +" .await\n" +" .expect(\"Failed to send cat.\");\n" +" });\n" +" tokio::spawn(async move {\n" +" sleep(Duration::from_millis(50)).await;\n" +" dog_sender\n" +" .send(String::from(\"Rex\"))\n" +" .await\n" +" .expect(\"Failed to send dog.\");\n" +" });\n" +"\n" +" let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)\n" +" .await\n" +" .expect(\"Failed to receive winner\");\n" +"\n" +" println!(\"Winner is {winner:?}\");\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use tokio::sync::mpsc::{self, Receiver};\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"#[derive(Debug, PartialEq)]\n" +"enum Animal {\n" +" Cat { name: String },\n" +" Dog { name: String },\n" +"}\n" +"\n" +"async fn first_animal_to_finish_race(\n" +" mut cat_rcv: Receiver,\n" +" mut dog_rcv: Receiver,\n" +") -> Option {\n" +" tokio::select! {\n" +" cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),\n" +" dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (cat_sender, cat_receiver) = mpsc::channel(32);\n" +" let (dog_sender, dog_receiver) = mpsc::channel(32);\n" +" tokio::spawn(async move {\n" +" sleep(Duration::from_millis(500)).await;\n" +" cat_sender\n" +" .send(String::from(\"Felix\"))\n" +" .await\n" +" .expect(\"Failed to send cat.\");\n" +" });\n" +" tokio::spawn(async move {\n" +" sleep(Duration::from_millis(50)).await;\n" +" dog_sender\n" +" .send(String::from(\"Rex\"))\n" +" .await\n" +" .expect(\"Failed to send dog.\");\n" +" });\n" +"\n" +" let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)\n" +" .await\n" +" .expect(\"Failed to receive winner\");\n" +"\n" +" println!(\"Winner is {winner:?}\");\n" +"}\n" +"```" + +#: src/async/control-flow/select.md:62 +msgid "" +"* In this example, we have a race between a cat and a dog.\n" +" `first_animal_to_finish_race` listens to both channels and will pick " +"whichever\n" +" arrives first. Since the dog takes 50ms, it wins against the cat that\n" +" take 500ms seconds.\n" +"\n" +"* You can use `oneshot` channels in this example as the channels are " +"supposed to\n" +" receive only one `send`.\n" +"\n" +"* Try adding a deadline to the race, demonstrating selecting different sorts " +"of\n" +" futures.\n" +"\n" +"* Note that `select!` moves the values it is given. It is easiest to use\n" +" when every execution of `select!` creates new futures. An alternative is " +"to\n" +" pass `&mut future` instead of the future itself, but this can lead to\n" +" issues, further discussed in the pinning slide." +msgstr "" +"* 이 예제에서는 고양이와 개를 경주시켰습니다(동시에 시작시킨 후, 둘 중 먼저 끝나는 쪽이 이김).\n" +" `first_animal_to_finish_race`함수는 두 채널 모두에 귀기울이고(listen하고) 있다가, 메시지가 먼저 도착한 채널" +"을\n" +" 선택합니다. 개는 50ms만에 작업을 끝내고 고양이는 500ms가 걸리기 때문에, 개가\n" +" 이깁니다.\n" +"\n" +"* 이 예제에서는 `oneshot` 채널을 사용하는 것이 더 좋은 디자인 입니다. 각 채널이\n" +" 한 번의 `send`만 수신해야 하는 것이 보장되기 때문입니다.\n" +"\n" +"* 이 경주에 타임아웃을 추가해서, 서로 다른 종류의 futures들을 동시에 `select`할 수 있음을\n" +" 확인해 보세요.\n" +"\n" +"* `select!`는 매칭되지 않은 다른 모든 브랜치에 대해 drop을 수행합니다. 따라서 경쟁에서 진 모든 future들은 \n" +" 취소(cancel) 됩니다. `select!`를 실행할 때마다\n" +" 새로운 futures가 만들어지는 경우라면, 이러한 점이 매우 편하게 느껴질 것입니다.\n" +" Future가 취소되는 것을 막기 위해 future를 직접 전달하는 대신 `&mut future`를 전달하는 것도 가능합니다. \n" +" 하지만 이렇게 하면 문제가\n" +" 발생할 수 있습니다 (왜 그런지는 `Pin`설명할 때 자세히 설명하겠습니다)." + +#: src/async/pitfalls.md:1 +msgid "# Pitfalls of async/await" +msgstr "# async/await에서 주의해야할 함정" + +#: src/async/pitfalls.md:3 +msgid "" +"Async / await provides convenient and efficient abstraction for concurrent " +"asynchronous programming. However, the async/await model in Rust also comes " +"with its share of pitfalls and footguns. We illustrate some of them in this " +"chapter:" +msgstr "" +"Async와 await는 동시 비동기 프로그래밍을 위한 편리하고 효율적인 추상화를 제공" +"합니다. 하지만 Rust의 async/await 모델에도 문제는 있습니다. 이 장에서 몇 가" +"지 예를 살펴보겠습니다." + +#: src/async/pitfalls.md:5 +msgid "" +"- [Blocking the Executor](pitfalls/blocking-executor.md)\n" +"- [Pin](pitfalls/pin.md)\n" +"- [Async Traits](pitfall/async-traits.md)" +msgstr "" +"- [실행자 차단](pitfalls/blocking-executor.md)\n" +"- [고정](pitfalls/pin.md)\n" +"- [비동기 트레잇](pitfall/async-traits.md)" + +#: src/async/pitfalls/blocking-executor.md:1 +msgid "# Blocking the executor" +msgstr "# 실행자(executor)를 블록시킴" + +#: src/async/pitfalls/blocking-executor.md:3 +msgid "" +"Most async runtimes only allow IO tasks to run concurrently.\n" +"This means that CPU blocking tasks will block the executor and prevent other " +"tasks from being executed.\n" +"An easy workaround is to use async equivalent methods where possible." +msgstr "" +"대부분의 비동기 런타임은 IO 작업만 동시에 실행되도록 허용합니다.\n" +"즉, CPU를 블럭하는 태스크가 있는 경우, 이는 실행자(executor)를 블럭하게 되며, 그 결과로 다른 태스크가 실행되지 않습니다.\n" +"이 문제를 해결하는 간단한 방법은, 항상 async를 지원하는 메서드를 사용하는 것입니다." + +#: src/async/pitfalls/blocking-executor.md:7 +msgid "" +"```rust,editable,compile_fail\n" +"use futures::future::join_all;\n" +"use std::time::Instant;\n" +"\n" +"async fn sleep_ms(start: &Instant, id: u64, duration_ms: u64) {\n" +" std::thread::sleep(std::time::Duration::from_millis(duration_ms));\n" +" println!(\n" +" \"future {id} slept for {duration_ms}ms, finished after {}ms\",\n" +" start.elapsed().as_millis()\n" +" );\n" +"}\n" +"\n" +"#[tokio::main(flavor = \"current_thread\")]\n" +"async fn main() {\n" +" let start = Instant::now();\n" +" let sleep_futures = (1..=10).map(|t| sleep_ms(&start, t, t * 10));\n" +" join_all(sleep_futures).await;\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use futures::future::join_all;\n" +"use std::time::Instant;\n" +"\n" +"async fn sleep_ms(start: &Instant, id: u64, duration_ms: u64) {\n" +" std::thread::sleep(std::time::Duration::from_millis(duration_ms));\n" +" println!(\n" +" \"future {id} slept for {duration_ms}ms, finished after {}ms\",\n" +" start.elapsed().as_millis()\n" +" );\n" +"}\n" +"\n" +"#[tokio::main(flavor = \"current_thread\")]\n" +"async fn main() {\n" +" let start = Instant::now();\n" +" let sleep_futures = (1..=10).map(|t| sleep_ms(&start, t, t * 10));\n" +" join_all(sleep_futures).await;\n" +"}\n" +"```" + +#: src/async/pitfalls/blocking-executor.md:29 +msgid "" +"* Run the code and see that the sleeps happen consecutively rather than\n" +" concurrently.\n" +"\n" +"* The `\"current_thread\"` flavor puts all tasks on a single thread. This " +"makes the\n" +" effect more obvious, but the bug is still present in the multi-threaded\n" +" flavor.\n" +"\n" +"* Switch the `std::thread::sleep` to `tokio::time::sleep` and await its " +"result.\n" +"\n" +"* Another fix would be to `tokio::task::spawn_blocking` which spawns an " +"actual\n" +" thread and transforms its handle into a future without blocking the " +"executor.\n" +"\n" +"* You should not think of tasks as OS threads. They do not map 1 to 1 and " +"most\n" +" executors will allow many tasks to run on a single OS thread. This is\n" +" particularly problematic when interacting with other libraries via FFI, " +"where\n" +" that library might depend on thread-local storage or map to specific OS\n" +" threads (e.g., CUDA). Prefer `tokio::task::spawn_blocking` in such " +"situations.\n" +"\n" +"* Use sync mutexes with care. Holding a mutex over an `.await` may cause " +"another\n" +" task to block, and that task may be running on the same thread." +msgstr "" +"* 코드를 실행하여 sleep들이 동시에 진행되지 않고 순차적으로으로\n" +" 진행되는지 확인하세요.\n" +"\n" +"* `flavor`를 `\"current_thread\"` 로 설정하면 모든 태스크가 하나의 스레드에서 수행됩니다. 이렇" +"게 하면 문제 상황이 더 분명히 드러납니다. 그러나\n" +" 이 버그는 멀티스레드인 경우에도 여전히\n" +" 존재합니다.\n" +"\n" +"* `std::thread::sleep`을 `tokio::time::sleep`으로 바꾸고 그 결과를 `await`해 보세요.\n" +"\n" +"* 또 다른 해결 방법은 `tokio::task::spawn_blocking`입니다. 이는 실제 스레드를\n" +" 생성하고, 그 스레드에 대한 핸들을 future로 변환함으로써 실행자가 블록되는 것을 막습니다.\n" +"\n" +"* 태스크를 OS 스레드라고 생각하면 안 됩니다. 태스크와 OS스레드는 일대일 매핑 관계에 있지 않습니다.\n" +" 대부분의 실행자는 하나의 OS 스레드에서 최대한 많은 태스크를 수행하도록 설계되어 있습니다. 이" +"점은\n" +" FFI를 통해 다른 라이브러리와 상호작용할 때 특히 문제가 됩니다.\n" +" 예를 들어, 해당 라이브러리가 스레드 로컬 저장소를 이용하거나 특정 OS 스레드에\n" +" 매핑될 수 있습니다(예: CUDA). 이러한 상황에서는 `tokio::task::" +"spawn_blocking`을 사용하는 것이 좋습니다.\n" +"\n" +"* 동기화 뮤텍스를 주의해서 사용하세요. `.await` 위에 뮤텍스를 적용하면 다른 " +"작업이\n" +" 차단될 수 있으며 해당 작업은 동일한 스레드에서 실행 중일 수 있습니다." + +#: src/async/pitfalls/pin.md:1 +msgid "# Pin" +msgstr "# Future 고정(Pin)하기" + +#: src/async/pitfalls/pin.md:3 +msgid "" +"When you await a future, all local variables (that would ordinarily be " +"stored on\n" +"a stack frame) are instead stored in the Future for the current async block. " +"If your\n" +"future has pointers to data on the stack, those pointers might get " +"invalidated.\n" +"This is unsafe." +msgstr "" +"Future에 대해 `await`를 호출하여 그 future가 준비되기를 기다릴 때, 모든 로컬 변수(일반적으로 스택 프레임에 저장됨)는\n" +"그 future객체 안에 저장됩니다. 만약 그 future에\n" +"스택에 있는 어떤 데이터로의 포인터가 있으면 이러한 포인터는 올바르지 않을 수 있습니다.\n" +"안전하지 않습니다." + +#: src/async/pitfalls/pin.md:8 +msgid "" +"Therefore, you must guarantee that the addresses your future points to " +"don't\n" +"change. That is why we need to `pin` futures. Using the same future " +"repeatedly\n" +"in a `select!` often leads to issues with pinned values." +msgstr "" +"따라서 future가 가리키는 주소가 변경되지 않도록\n" +"해야 합니다. 이것이 future를 `pin`(고정)해야 하는 이유입니다. `select!`에서 동" +"일한 future를\n" +"반복적으로 사용하면 고정 값 문제가 발생하는 경우가 많습니다." + +#: src/async/pitfalls/pin.md:12 +msgid "" +"```rust,editable,compile_fail\n" +"use tokio::sync::{mpsc, oneshot};\n" +"use tokio::task::spawn;\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"// A work item. In this case, just sleep for the given time and respond\n" +"// with a message on the `respond_on` channel.\n" +"#[derive(Debug)]\n" +"struct Work {\n" +" input: u32,\n" +" respond_on: oneshot::Sender,\n" +"}\n" +"\n" +"// A worker which listens for work on a queue and performs it.\n" +"async fn worker(mut work_queue: mpsc::Receiver) {\n" +" let mut iterations = 0;\n" +" loop {\n" +" tokio::select! {\n" +" Some(work) = work_queue.recv() => {\n" +" sleep(Duration::from_millis(10)).await; // Pretend to work.\n" +" work.respond_on\n" +" .send(work.input * 1000)\n" +" .expect(\"failed to send response\");\n" +" iterations += 1;\n" +" }\n" +" // TODO: report number of iterations every 100ms\n" +" }\n" +" }\n" +"}\n" +"\n" +"// A requester which requests work and waits for it to complete.\n" +"async fn do_work(work_queue: &mpsc::Sender, input: u32) -> u32 {\n" +" let (tx, rx) = oneshot::channel();\n" +" work_queue\n" +" .send(Work {\n" +" input,\n" +" respond_on: tx,\n" +" })\n" +" .await\n" +" .expect(\"failed to send on work queue\");\n" +" rx.await.expect(\"failed waiting for response\")\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (tx, rx) = mpsc::channel(10);\n" +" spawn(worker(rx));\n" +" for i in 0..100 {\n" +" let resp = do_work(&tx, i).await;\n" +" println!(\"work result for iteration {i}: {resp}\");\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use tokio::sync::{mpsc, oneshot};\n" +"use tokio::task::spawn;\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"// 작업 항목. 이 경우 지정된 시간 동안 절전 모드로 있다가\n" +"// `respond_on` 채널의 메시지로 응답합니다.\n" +"#[derive(Debug)]\n" +"struct Work {\n" +" input: u32,\n" +" respond_on: oneshot::Sender,\n" +"}\n" +"\n" +"// 대기열에서 작업을 리슨하고 실행하는 worker입니다.\n" +"async fn worker(mut work_queue: mpsc::Receiver) {\n" +" let mut iterations = 0;\n" +" loop {\n" +" tokio::select! {\n" +" Some(work) = work_queue.recv() => {\n" +" sleep(Duration::from_millis(10)).await; // Pretend to work.\n" +" work.respond_on\n" +" .send(work.input * 1000)\n" +" .expect(\"failed to send response\");\n" +" iterations += 1;\n" +" }\n" +" // TODO: 100ms마다 반복 횟수를 보고합니다.\n" +" }\n" +" }\n" +"}\n" +"\n" +"// 작업을 요청하고 작업이 완료될 때까지 기다리는 요청자입니다.\n" +"async fn do_work(work_queue: &mpsc::Sender, input: u32) -> u32 {\n" +" let (tx, rx) = oneshot::channel();\n" +" work_queue\n" +" .send(Work {\n" +" input,\n" +" respond_on: tx,\n" +" })\n" +" .await\n" +" .expect(\"failed to send on work queue\");\n" +" rx.await.expect(\"failed waiting for response\")\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let (tx, rx) = mpsc::channel(10);\n" +" spawn(worker(rx));\n" +" for i in 0..100 {\n" +" let resp = do_work(&tx, i).await;\n" +" println!(\"work result for iteration {i}: {resp}\");\n" +" }\n" +"}\n" +"```" + +#: src/async/pitfalls/pin.md:68 +msgid "" +"* You may recognize this as an example of the actor pattern. Actors\n" +" typically call `select!` in a loop.\n" +"\n" +"* This serves as a summation of a few of the previous lessons, so take your " +"time\n" +" with it.\n" +"\n" +" * Naively add a `_ = sleep(Duration::from_millis(100)) => { println!" +"(..) }`\n" +" to the `select!`. This will never execute. Why?\n" +"\n" +" * Instead, add a `timeout_fut` containing that future outside of the " +"`loop`:\n" +"\n" +" ```rust,compile_fail\n" +" let mut timeout_fut = sleep(Duration::from_millis(100));\n" +" loop {\n" +" select! {\n" +" ..,\n" +" _ = timeout_fut => { println!(..); },\n" +" }\n" +" }\n" +" ```\n" +" * This still doesn't work. Follow the compiler errors, adding `&mut` to " +"the\n" +" `timeout_fut` in the `select!` to work around the move, then using\n" +" `Box::pin`:\n" +"\n" +" ```rust,compile_fail\n" +" let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100)));\n" +" loop {\n" +" select! {\n" +" ..,\n" +" _ = &mut timeout_fut => { println!(..); },\n" +" }\n" +" }\n" +" ```\n" +"\n" +" * This compiles, but once the timeout expires it is `Poll::Ready` on " +"every\n" +" iteration (a fused future would help with this). Update to reset\n" +" `timeout_fut` every time it expires.\n" +"\n" +"* Box allocates on the heap. In some cases, `std::pin::pin!` (only recently\n" +" stabilized, with older code often using `tokio::pin!`) is also an option, " +"but\n" +" that is difficult to use for a future that is reassigned.\n" +"\n" +"* Another alternative is to not use `pin` at all but spawn another task that " +"will send to a `oneshot` channel every 100ms." +msgstr "" +"* 위에서 소개한 것은 액터(actor) 패턴의 한 예라고 봐도 무방합니다. 액터는 일반적으로 루프 안에서\n" +" `select!`를 호출합니다.\n" +"\n" +"* 이전 강의 몇 개의 내용을 요약한 것이기 때문에 천천히\n" +" 살펴보세요.\n" +"\n" +" * `_ = sleep(Duration::from_millis(100)) => { println!(..) }`을 `select!`" +"에 추가해 보세요.\n" +" 이 작업은 실행되지 않습니다. 왜 그럴까요?\n" +"\n" +" * 대신, 해당 future가 포함된 `timeout_fut`를 `loop` 외부에 추가해 보세요.\n" +"\n" +" ```rust,compile_fail\n" +" let mut timeout_fut = sleep(Duration::from_millis(100));\n" +" loop {\n" +" select! {\n" +" ..,\n" +" _ = timeout_fut => { println!(..); },\n" +" }\n" +" }\n" +" ```\n" +" * 여전히 작동하지 않습니다. 컴파일러 오류를 따라 `select!`의 " +"`timeout_fut`에\n" +" `&mut`를 추가하여 Move 시멘틱 관련한 문제를 해결하고 `Box::pin`을\n" +" 사용하세요.\n" +"\n" +" ```rust,compile_fail\n" +" let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100)));\n" +" loop {\n" +" select! {\n" +" ..,\n" +" _ = &mut timeout_fut => { println!(..); },\n" +" }\n" +" }\n" +" ```\n" +"\n" +" * 이는 컴파일은 되지만 타임 아웃이 되면 매번 반복할 때 마다\n" +" `Poll::Ready`가 됩니다(융합된 future가 도움이 될 수 있음). 타임 아웃 될 때마" +"다\n" +" `timeout_fut`를 리셋하도록 수정하세요.\n" +"\n" +"* Box는 힙에 할당합니다. 경우에 따라 `std::pin::pin!`(최근에야 안정화되었으" +"며 이전 코드는\n" +" `tokio::pin!`을 사용하는 경우가 많음)도 사용할 수 있지만\n" +" 이는 재할당된 future에 사용하기가 어렵습니다.\n" +"\n" +"* 또 다른 방법은 `pin`을 아예 사용하지 않고 100ms마다 `oneshot` 채널에 전송" +"할 다른 작업을 생성하는 것입니다." + +#: src/async/pitfalls/async-traits.md:3 +msgid "" +"Async methods in traits are not yet supported in the stable channel ([An " +"experimental feature exists in nightly and should be stabilized in the mid " +"term.](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-" +"nightly.html))" +msgstr "" +"트레잇에 async 메소드를 추가하는 것은 아직 안정화 버전 채널에서 지원되지 않습니다([실험용 " +"기능은 nightly에 존재하며 조만간 안정화 될 것입니다](https://blog.rust-lang.org/inside-" +"rust/2022/11/17/async-fn-in-trait-nightly.html))." + +#: src/async/pitfalls/async-traits.md:5 +msgid "" +"The crate [async_trait](https://docs.rs/async-trait/latest/async_trait/) " +"provides a workaround through a macro:" +msgstr "" +"크레이트 [async_trait](https://docs.rs/async-trait/latest/async_trait/)은 매" +"크로를 통한 해결 방법을 제공합니다." + +#: src/async/pitfalls/async-traits.md:7 +msgid "" +"```rust,editable,compile_fail\n" +"use async_trait::async_trait;\n" +"use std::time::Instant;\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"#[async_trait]\n" +"trait Sleeper {\n" +" async fn sleep(&self);\n" +"}\n" +"\n" +"struct FixedSleeper {\n" +" sleep_ms: u64,\n" +"}\n" +"\n" +"#[async_trait]\n" +"impl Sleeper for FixedSleeper {\n" +" async fn sleep(&self) {\n" +" sleep(Duration::from_millis(self.sleep_ms)).await;\n" +" }\n" +"}\n" +"\n" +"async fn run_all_sleepers_multiple_times(sleepers: Vec>, " +"n_times: usize) {\n" +" for _ in 0..n_times {\n" +" println!(\"running all sleepers..\");\n" +" for sleeper in &sleepers {\n" +" let start = Instant::now();\n" +" sleeper.sleep().await;\n" +" println!(\"slept for {}ms\", start.elapsed().as_millis());\n" +" }\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let sleepers: Vec> = vec![\n" +" Box::new(FixedSleeper { sleep_ms: 50 }),\n" +" Box::new(FixedSleeper { sleep_ms: 100 }),\n" +" ];\n" +" run_all_sleepers_multiple_times(sleepers, 5).await;\n" +"}\n" +"```" +msgstr "" +"```rust,editable,compile_fail\n" +"use async_trait::async_trait;\n" +"use std::time::Instant;\n" +"use tokio::time::{sleep, Duration};\n" +"\n" +"#[async_trait]\n" +"trait Sleeper {\n" +" async fn sleep(&self);\n" +"}\n" +"\n" +"struct FixedSleeper {\n" +" sleep_ms: u64,\n" +"}\n" +"\n" +"#[async_trait]\n" +"impl Sleeper for FixedSleeper {\n" +" async fn sleep(&self) {\n" +" sleep(Duration::from_millis(self.sleep_ms)).await;\n" +" }\n" +"}\n" +"\n" +"async fn run_all_sleepers_multiple_times(sleepers: Vec>, " +"n_times: usize) {\n" +" for _ in 0..n_times {\n" +" println!(\"running all sleepers..\");\n" +" for sleeper in &sleepers {\n" +" let start = Instant::now();\n" +" sleeper.sleep().await;\n" +" println!(\"slept for {}ms\", start.elapsed().as_millis());\n" +" }\n" +" }\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() {\n" +" let sleepers: Vec> = vec![\n" +" Box::new(FixedSleeper { sleep_ms: 50 }),\n" +" Box::new(FixedSleeper { sleep_ms: 100 }),\n" +" ];\n" +" run_all_sleepers_multiple_times(sleepers, 5).await;\n" +"}\n" +"```" + +#: src/async/pitfalls/async-traits.md:51 +msgid "" +"* `async_trait` is easy to use, but note that it's using heap allocations " +"to\n" +" achieve this. This heap allocation has performance overhead.\n" +"\n" +"* The challenges in language support for `async trait` are deep Rust and\n" +" probably not worth describing in-depth. Niko Matsakis did a good job of\n" +" explaining them in [this\n" +" post](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-" +"traits-are-hard/)\n" +" if you are interested in digging deeper.\n" +"\n" +"* Try creating a new sleeper struct that will sleep for a random amount of " +"time\n" +" and adding it to the Vec." +msgstr "" +"* `async_trait`은 사용하기 쉽지만 이를 위해 힙에 메모리를 할당한다는 점에\n" +" 유의하세요. 이 힙 할당에는 성능 오버헤드가 있습니다.\n" +"\n" +"* `async trait` 를 언어 차원에서 지원하는 것과 관련된 문제는 매우 전문적인 토픽이며\n" +" 따라서 이 강의에서 다룰 내용은 아닙니다. [이 게시물](https://smallcultfollowing." +"com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/)에\n" +" 이에 관한 니코 마사키스의 좋은 설명이\n" +" 있으므로 관심이 있다면\n" +" 참고하세요.\n" +"\n" +"* 임의의 시간 동안 sleep 하는 새로운 sleeper 구조체를 만들어\n" +" Vec에 추가해 보세요." + #: src/exercises/day-4/morning.md:1 src/exercises/day-4/android.md:1 msgid "# Exercises" msgstr "# 연습문제"