mirror of
https://github.com/google/comprehensive-rust.git
synced 2024-12-15 06:20:32 +02:00
274f16b839
Add some missing translation
13253 lines
518 KiB
Plaintext
13253 lines
518 KiB
Plaintext
msgid ""
|
|
msgstr ""
|
|
"Project-Id-Version: [한국어]Comprehensive Rust 🦀\n"
|
|
"POT-Creation-Date: \n"
|
|
"PO-Revision-Date: \n"
|
|
"Last-Translator: \n"
|
|
"Language-Team: \n"
|
|
"Language: ko\n"
|
|
"MIME-Version: 1.0\n"
|
|
"Content-Type: text/plain; charset=UTF-8\n"
|
|
"Content-Transfer-Encoding: 8bit\n"
|
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
|
"X-Generator: Poedit 3.2.2\n"
|
|
|
|
#: src/SUMMARY.md:3
|
|
msgid "Welcome to Comprehensive Rust 🦀"
|
|
msgstr "Comprehensive Rust에 오신 것을 환영합니다 🦀"
|
|
|
|
#: src/SUMMARY.md:4
|
|
msgid "Running the Course"
|
|
msgstr "강의 진행"
|
|
|
|
#: src/SUMMARY.md:5
|
|
msgid "Course Structure"
|
|
msgstr "강의 구성"
|
|
|
|
#: src/SUMMARY.md:6
|
|
msgid "Day 4"
|
|
msgstr "4일차"
|
|
|
|
#: src/SUMMARY.md:7
|
|
msgid "Keyboard Shortcuts"
|
|
msgstr "단축키"
|
|
|
|
#: src/SUMMARY.md:8
|
|
msgid "Translations"
|
|
msgstr "다른 언어들"
|
|
|
|
#: src/SUMMARY.md:9
|
|
msgid "Using Cargo"
|
|
msgstr "카고 사용하기"
|
|
|
|
#: src/SUMMARY.md:10
|
|
msgid "Rust Ecosystem"
|
|
msgstr "러스트 생태계"
|
|
|
|
#: src/SUMMARY.md:11
|
|
msgid "Code Samples"
|
|
msgstr "코드 샘플"
|
|
|
|
#: src/SUMMARY.md:12
|
|
msgid "Running Cargo Locally"
|
|
msgstr "카고(Cargo) 수행하기"
|
|
|
|
#: src/SUMMARY.md:15
|
|
msgid "Day 1: Morning"
|
|
msgstr "1일차 오전"
|
|
|
|
#: src/SUMMARY.md:19 src/SUMMARY.md:75 src/SUMMARY.md:128 src/SUMMARY.md:184
|
|
msgid "Welcome"
|
|
msgstr "개요"
|
|
|
|
#: src/SUMMARY.md:20
|
|
msgid "What is Rust?"
|
|
msgstr "러스트란?"
|
|
|
|
#: src/SUMMARY.md:21
|
|
msgid "Hello World!"
|
|
msgstr "Hello World!"
|
|
|
|
#: src/SUMMARY.md:22
|
|
msgid "Small Example"
|
|
msgstr "작은 예제"
|
|
|
|
#: src/SUMMARY.md:23
|
|
msgid "Why Rust?"
|
|
msgstr "러스트를 써야하는 이유"
|
|
|
|
#: src/SUMMARY.md:24
|
|
msgid "Compile Time Guarantees"
|
|
msgstr "컴파일 시 보장되는 것들"
|
|
|
|
#: src/SUMMARY.md:25
|
|
msgid "Runtime Guarantees"
|
|
msgstr "런타임 시 보장되는 것들"
|
|
|
|
#: src/SUMMARY.md:26
|
|
msgid "Modern Features"
|
|
msgstr "현대적인 특징"
|
|
|
|
#: src/SUMMARY.md:27
|
|
msgid "Basic Syntax"
|
|
msgstr "기본 문법"
|
|
|
|
#: src/SUMMARY.md:28
|
|
msgid "Scalar Types"
|
|
msgstr "스칼라 타입"
|
|
|
|
#: src/SUMMARY.md:29
|
|
msgid "Compound Types"
|
|
msgstr "복합 타입"
|
|
|
|
#: src/SUMMARY.md:30
|
|
msgid "References"
|
|
msgstr "참조"
|
|
|
|
#: src/SUMMARY.md:31
|
|
msgid "Dangling References"
|
|
msgstr "허상(dangling) 참조"
|
|
|
|
#: src/SUMMARY.md:32
|
|
msgid "Slices"
|
|
msgstr "슬라이스"
|
|
|
|
#: src/SUMMARY.md:33
|
|
msgid "String vs str"
|
|
msgstr "String과 str"
|
|
|
|
#: src/SUMMARY.md:34
|
|
msgid "Functions"
|
|
msgstr "함수"
|
|
|
|
#: src/SUMMARY.md:35 src/SUMMARY.md:82
|
|
msgid "Methods"
|
|
msgstr "메서드"
|
|
|
|
#: src/SUMMARY.md:36
|
|
msgid "Overloading"
|
|
msgstr "오버로딩"
|
|
|
|
#: src/SUMMARY.md:37 src/SUMMARY.md:66 src/SUMMARY.md:90 src/SUMMARY.md:119
|
|
#: src/SUMMARY.md:148 src/SUMMARY.md:176 src/SUMMARY.md:199 src/SUMMARY.md:226
|
|
msgid "Exercises"
|
|
msgstr "연습문제"
|
|
|
|
#: src/SUMMARY.md:38
|
|
msgid "Implicit Conversions"
|
|
msgstr "묵시적 형변환"
|
|
|
|
#: src/SUMMARY.md:39
|
|
msgid "Arrays and for Loops"
|
|
msgstr "배열과 for 반복문"
|
|
|
|
#: src/SUMMARY.md:41
|
|
msgid "Day 1: Afternoon"
|
|
msgstr "1일차 오후"
|
|
|
|
#: src/SUMMARY.md:43
|
|
msgid "Variables"
|
|
msgstr "변수"
|
|
|
|
#: src/SUMMARY.md:44
|
|
msgid "Type Inference"
|
|
msgstr "타입 추론"
|
|
|
|
#: src/SUMMARY.md:45
|
|
msgid "static & const"
|
|
msgstr "정적변수(static)와 상수(const)"
|
|
|
|
#: src/SUMMARY.md:46
|
|
msgid "Scopes and Shadowing"
|
|
msgstr "범위(Scopes)와 쉐도잉(Shadowing)"
|
|
|
|
#: src/SUMMARY.md:47
|
|
msgid "Memory Management"
|
|
msgstr "메모리 관리"
|
|
|
|
#: src/SUMMARY.md:48
|
|
msgid "Stack vs Heap"
|
|
msgstr "스택(Stack)과 힙(Heap)"
|
|
|
|
#: src/SUMMARY.md:49
|
|
msgid "Stack Memory"
|
|
msgstr "스택 메모리"
|
|
|
|
#: src/SUMMARY.md:50
|
|
msgid "Manual Memory Management"
|
|
msgstr "수동 메모리 관리"
|
|
|
|
#: src/SUMMARY.md:51
|
|
msgid "Scope-Based Memory Management"
|
|
msgstr "범위기반 메모리 관리"
|
|
|
|
#: src/SUMMARY.md:52
|
|
msgid "Garbage Collection"
|
|
msgstr "가비지 컬렉션"
|
|
|
|
#: src/SUMMARY.md:53
|
|
msgid "Rust Memory Management"
|
|
msgstr "러스트의 메모리 관리"
|
|
|
|
#: src/SUMMARY.md:54
|
|
msgid "Comparison"
|
|
msgstr "비교"
|
|
|
|
#: src/SUMMARY.md:55
|
|
msgid "Ownership"
|
|
msgstr "소유권"
|
|
|
|
#: src/SUMMARY.md:56
|
|
msgid "Move Semantics"
|
|
msgstr "Move 문법"
|
|
|
|
#: src/SUMMARY.md:57
|
|
msgid "Moved Strings in Rust"
|
|
msgstr "러스트에서의 문자열 이동"
|
|
|
|
#: src/SUMMARY.md:58
|
|
msgid "Double Frees in Modern C++"
|
|
msgstr "Modern C++에서 이중해제 문제"
|
|
|
|
#: src/SUMMARY.md:59
|
|
msgid "Moves in Function Calls"
|
|
msgstr "함수 호출에서의 이동(Move)"
|
|
|
|
#: src/SUMMARY.md:60
|
|
msgid "Copying and Cloning"
|
|
msgstr "복사와 복제"
|
|
|
|
#: src/SUMMARY.md:61
|
|
msgid "Borrowing"
|
|
msgstr "빌림"
|
|
|
|
#: src/SUMMARY.md:62
|
|
msgid "Shared and Unique Borrows"
|
|
msgstr "공유와 고유 빌림"
|
|
|
|
#: src/SUMMARY.md:63
|
|
msgid "Lifetimes"
|
|
msgstr "수명"
|
|
|
|
#: src/SUMMARY.md:64
|
|
msgid "Lifetimes in Function Calls"
|
|
msgstr "함수 호출에서의 수명"
|
|
|
|
#: src/SUMMARY.md:65
|
|
msgid "Lifetimes in Data Structures"
|
|
msgstr "구조체에서의 수명"
|
|
|
|
#: src/SUMMARY.md:67
|
|
msgid "Designing a Library"
|
|
msgstr "도서관 설계"
|
|
|
|
#: src/SUMMARY.md:68
|
|
msgid "Iterators and Ownership"
|
|
msgstr "반복자와 소유권"
|
|
|
|
#: src/SUMMARY.md:71
|
|
msgid "Day 2: Morning"
|
|
msgstr "2일차 오전"
|
|
|
|
#: src/SUMMARY.md:76
|
|
msgid "Structs"
|
|
msgstr "구조체"
|
|
|
|
#: src/SUMMARY.md:77
|
|
msgid "Tuple Structs"
|
|
msgstr "튜플"
|
|
|
|
#: src/SUMMARY.md:78
|
|
msgid "Field Shorthand Syntax"
|
|
msgstr "필드 할당 단축 문법"
|
|
|
|
#: src/SUMMARY.md:79
|
|
msgid "Enums"
|
|
msgstr "열거형"
|
|
|
|
#: src/SUMMARY.md:80
|
|
msgid "Variant Payloads"
|
|
msgstr "데이터를 포함하는 열거형(Variant Payloads)"
|
|
|
|
#: src/SUMMARY.md:81
|
|
msgid "Enum Sizes"
|
|
msgstr "열거형의 크기"
|
|
|
|
#: src/SUMMARY.md:83
|
|
msgid "Method Receiver"
|
|
msgstr "메서드 리시버(Receiver)"
|
|
|
|
#: src/SUMMARY.md:84 src/SUMMARY.md:159 src/SUMMARY.md:194
|
|
msgid "Example"
|
|
msgstr "예제"
|
|
|
|
#: src/SUMMARY.md:85
|
|
msgid "Pattern Matching"
|
|
msgstr "패턴 매칭"
|
|
|
|
#: src/SUMMARY.md:86
|
|
msgid "Destructuring Enums"
|
|
msgstr "열거형 분해(역구조화)"
|
|
|
|
#: src/SUMMARY.md:87
|
|
msgid "Destructuring Structs"
|
|
msgstr "구조체 분해(역구조화)"
|
|
|
|
#: src/SUMMARY.md:88
|
|
msgid "Destructuring Arrays"
|
|
msgstr "배열 분해(역구조화)"
|
|
|
|
#: src/SUMMARY.md:89
|
|
msgid "Match Guards"
|
|
msgstr "매치 가드"
|
|
|
|
#: src/SUMMARY.md:91
|
|
msgid "Health Statistics"
|
|
msgstr "건강상태 모니터링 시스템"
|
|
|
|
#: src/SUMMARY.md:92
|
|
msgid "Points and Polygons"
|
|
msgstr "점과 다각형"
|
|
|
|
#: src/SUMMARY.md:94
|
|
msgid "Day 2: Afternoon"
|
|
msgstr "2일차 오후"
|
|
|
|
#: src/SUMMARY.md:96
|
|
msgid "Control Flow"
|
|
msgstr "흐름 제어"
|
|
|
|
#: src/SUMMARY.md:97
|
|
msgid "Blocks"
|
|
msgstr "블록"
|
|
|
|
#: src/SUMMARY.md:98
|
|
msgid "if expressions"
|
|
msgstr "if 표현식"
|
|
|
|
#: src/SUMMARY.md:99
|
|
msgid "if let expressions"
|
|
msgstr "if let 표현식"
|
|
|
|
#: src/SUMMARY.md:100
|
|
msgid "while expressions"
|
|
msgstr "while 표현식"
|
|
|
|
#: src/SUMMARY.md:101
|
|
msgid "while let expressions"
|
|
msgstr "while let 표현식"
|
|
|
|
#: src/SUMMARY.md:102
|
|
msgid "for expressions"
|
|
msgstr "for 표현식"
|
|
|
|
#: src/SUMMARY.md:103
|
|
msgid "loop expressions"
|
|
msgstr "loop 표현식"
|
|
|
|
#: src/SUMMARY.md:104
|
|
msgid "match expressions"
|
|
msgstr "match 표현식"
|
|
|
|
#: src/SUMMARY.md:105
|
|
msgid "break & continue"
|
|
msgstr "break와 continue"
|
|
|
|
#: src/SUMMARY.md:106
|
|
msgid "Standard Library"
|
|
msgstr "표준 라이브러리"
|
|
|
|
#: src/SUMMARY.md:107
|
|
msgid "Option and Result"
|
|
msgstr "Option과 Result"
|
|
|
|
#: src/SUMMARY.md:108
|
|
msgid "String"
|
|
msgstr "String"
|
|
|
|
#: src/SUMMARY.md:109
|
|
msgid "Vec"
|
|
msgstr "Vec"
|
|
|
|
#: src/SUMMARY.md:110
|
|
msgid "HashMap"
|
|
msgstr "HashMap"
|
|
|
|
#: src/SUMMARY.md:111
|
|
msgid "Box"
|
|
msgstr "Box"
|
|
|
|
#: src/SUMMARY.md:112
|
|
msgid "Recursive Data Types"
|
|
msgstr "재귀적 자료구조"
|
|
|
|
#: src/SUMMARY.md:113
|
|
msgid "Niche Optimization"
|
|
msgstr "니치(틈새) 최적화(Niche Optimization)"
|
|
|
|
#: src/SUMMARY.md:114
|
|
msgid "Rc"
|
|
msgstr "Rc"
|
|
|
|
#: src/SUMMARY.md:115
|
|
msgid "Modules"
|
|
msgstr "모듈"
|
|
|
|
#: src/SUMMARY.md:116
|
|
msgid "Visibility"
|
|
msgstr "가시성"
|
|
|
|
#: src/SUMMARY.md:117
|
|
msgid "Paths"
|
|
msgstr "경로"
|
|
|
|
#: src/SUMMARY.md:118
|
|
msgid "Filesystem Hierarchy"
|
|
msgstr "파일시스템 계층"
|
|
|
|
#: src/SUMMARY.md:120
|
|
msgid "Luhn Algorithm"
|
|
msgstr "룬 알고리즘"
|
|
|
|
#: src/SUMMARY.md:121
|
|
msgid "Strings and Iterators"
|
|
msgstr "문자열과 반복자"
|
|
|
|
#: src/SUMMARY.md:124
|
|
msgid "Day 3: Morning"
|
|
msgstr "3일차 오전"
|
|
|
|
#: src/SUMMARY.md:129
|
|
msgid "Traits"
|
|
msgstr "트레잇(Trait)"
|
|
|
|
#: src/SUMMARY.md:130
|
|
msgid "Deriving Traits"
|
|
msgstr "트레잇 상속하기"
|
|
|
|
#: src/SUMMARY.md:131
|
|
msgid "Default Methods"
|
|
msgstr "기본 메서드"
|
|
|
|
#: src/SUMMARY.md:132
|
|
msgid "Important Traits"
|
|
msgstr "중요한 트레잇"
|
|
|
|
#: src/SUMMARY.md:133
|
|
msgid "Iterator"
|
|
msgstr "Iterator"
|
|
|
|
#: src/SUMMARY.md:134
|
|
msgid "FromIterator"
|
|
msgstr "FromIterator"
|
|
|
|
#: src/SUMMARY.md:135
|
|
msgid "From and Into"
|
|
msgstr "From과 Into"
|
|
|
|
#: src/SUMMARY.md:136
|
|
msgid "Read and Write"
|
|
msgstr "Read와 Write"
|
|
|
|
#: src/SUMMARY.md:137
|
|
msgid "Add, Mul, ..."
|
|
msgstr "Add, Mul, ..."
|
|
|
|
#: src/SUMMARY.md:138
|
|
msgid "Drop"
|
|
msgstr "Drop"
|
|
|
|
#: src/SUMMARY.md:139
|
|
msgid "Default"
|
|
msgstr "Default"
|
|
|
|
#: src/SUMMARY.md:140
|
|
msgid "Generics"
|
|
msgstr "제네릭"
|
|
|
|
#: src/SUMMARY.md:141
|
|
msgid "Generic Data Types"
|
|
msgstr "제네릭 데이터 타입"
|
|
|
|
#: src/SUMMARY.md:142
|
|
msgid "Generic Methods"
|
|
msgstr "제네릭 메서드"
|
|
|
|
#: src/SUMMARY.md:143
|
|
msgid "Trait Bounds"
|
|
msgstr "제네릭 타입 제한(트레잇 경계)"
|
|
|
|
#: src/SUMMARY.md:144
|
|
msgid "impl Trait"
|
|
msgstr "트레잇 구현하기"
|
|
|
|
#: src/SUMMARY.md:145
|
|
msgid "Closures"
|
|
msgstr "클로저"
|
|
|
|
#: src/SUMMARY.md:146
|
|
msgid "Monomorphization"
|
|
msgstr "단형화"
|
|
|
|
#: src/SUMMARY.md:147
|
|
msgid "Trait Objects"
|
|
msgstr "트레잇 객체"
|
|
|
|
#: src/SUMMARY.md:149
|
|
msgid "A Simple GUI Library"
|
|
msgstr "간단한 GUI 라이브러리"
|
|
|
|
#: src/SUMMARY.md:151
|
|
msgid "Day 3: Afternoon"
|
|
msgstr "3일차 오후"
|
|
|
|
#: src/SUMMARY.md:153
|
|
msgid "Error Handling"
|
|
msgstr "오류처리"
|
|
|
|
#: src/SUMMARY.md:154
|
|
msgid "Panics"
|
|
msgstr "패닉"
|
|
|
|
#: src/SUMMARY.md:155
|
|
msgid "Catching Stack Unwinding"
|
|
msgstr "스택 되감기"
|
|
|
|
#: src/SUMMARY.md:156
|
|
msgid "Structured Error Handling"
|
|
msgstr "구조화된 오류처리"
|
|
|
|
#: src/SUMMARY.md:157
|
|
msgid "Propagating Errors with ?"
|
|
msgstr "'?'를 이용한 오류 전파"
|
|
|
|
#: src/SUMMARY.md:158
|
|
msgid "Converting Error Types"
|
|
msgstr "오류타입 변환"
|
|
|
|
#: src/SUMMARY.md:160
|
|
msgid "Deriving Error Enums"
|
|
msgstr "또다른 오류 열거형"
|
|
|
|
#: src/SUMMARY.md:161
|
|
msgid "Dynamic Error Types"
|
|
msgstr "동적인 에러 타입"
|
|
|
|
#: src/SUMMARY.md:162
|
|
msgid "Adding Context to Errors"
|
|
msgstr "오류에 상황정보 추가"
|
|
|
|
#: src/SUMMARY.md:163
|
|
msgid "Testing"
|
|
msgstr "테스트"
|
|
|
|
#: src/SUMMARY.md:164
|
|
msgid "Unit Tests"
|
|
msgstr "단위 테스트"
|
|
|
|
#: src/SUMMARY.md:165
|
|
msgid "Test Modules"
|
|
msgstr "테스트 모듈"
|
|
|
|
#: src/SUMMARY.md:166
|
|
msgid "Documentation Tests"
|
|
msgstr "문서화주석 테스트"
|
|
|
|
#: src/SUMMARY.md:167
|
|
msgid "Integration Tests"
|
|
msgstr "통합 테스트"
|
|
|
|
#: src/SUMMARY.md:168
|
|
msgid "Unsafe Rust"
|
|
msgstr "안전하지 않은 러스트"
|
|
|
|
#: src/SUMMARY.md:169
|
|
msgid "Dereferencing Raw Pointers"
|
|
msgstr "원시 포인터 역참조(따라가기)"
|
|
|
|
#: src/SUMMARY.md:170
|
|
msgid "Mutable Static Variables"
|
|
msgstr "정적 가변 변수"
|
|
|
|
#: src/SUMMARY.md:171
|
|
msgid "Unions"
|
|
msgstr "Unions"
|
|
|
|
#: src/SUMMARY.md:172
|
|
msgid "Calling Unsafe Functions"
|
|
msgstr "안전하지 않은 함수 호출"
|
|
|
|
#: src/SUMMARY.md:173
|
|
msgid "Writing Unsafe Functions"
|
|
msgstr "안전하지 않은 함수 작성하기"
|
|
|
|
#: src/SUMMARY.md:174
|
|
msgid "Extern Functions"
|
|
msgstr "외부(다른언어) 함수들"
|
|
|
|
#: src/SUMMARY.md:175
|
|
msgid "Implementing Unsafe Traits"
|
|
msgstr "안전하지 않은 트레잇 구현하기"
|
|
|
|
#: src/SUMMARY.md:177
|
|
msgid "Safe FFI Wrapper"
|
|
msgstr "FFI래퍼"
|
|
|
|
#: src/SUMMARY.md:180
|
|
msgid "Day 4: Morning"
|
|
msgstr "4일차 오전"
|
|
|
|
#: src/SUMMARY.md:185
|
|
msgid "Concurrency"
|
|
msgstr "동시성"
|
|
|
|
#: src/SUMMARY.md:186
|
|
msgid "Threads"
|
|
msgstr "스레드"
|
|
|
|
#: src/SUMMARY.md:187
|
|
msgid "Scoped Threads"
|
|
msgstr "범위 스레드(Scoped Threads)"
|
|
|
|
#: src/SUMMARY.md:188
|
|
msgid "Channels"
|
|
msgstr "채널"
|
|
|
|
#: src/SUMMARY.md:189
|
|
msgid "Unbounded Channels"
|
|
msgstr "무경계 채널"
|
|
|
|
#: src/SUMMARY.md:190
|
|
msgid "Bounded Channels"
|
|
msgstr "경계 채널"
|
|
|
|
#: src/SUMMARY.md:191
|
|
msgid "Shared State"
|
|
msgstr "상태 공유"
|
|
|
|
#: src/SUMMARY.md:192
|
|
msgid "Arc"
|
|
msgstr "Arc"
|
|
|
|
#: src/SUMMARY.md:193
|
|
msgid "Mutex"
|
|
msgstr "Mutex"
|
|
|
|
#: src/SUMMARY.md:195
|
|
msgid "Send and Sync"
|
|
msgstr "Send와 Sync"
|
|
|
|
#: src/SUMMARY.md:195
|
|
msgid "Send"
|
|
msgstr "Send"
|
|
|
|
#: src/SUMMARY.md:195
|
|
msgid "Sync"
|
|
msgstr "Sync"
|
|
|
|
#: src/SUMMARY.md:198
|
|
msgid "Examples"
|
|
msgstr "예제"
|
|
|
|
#: src/SUMMARY.md:200
|
|
msgid "Dining Philosophers"
|
|
msgstr "식사하는 철학자들"
|
|
|
|
#: src/SUMMARY.md:201
|
|
msgid "Multi-threaded Link Checker"
|
|
msgstr "멀티스레드 링크 검사기"
|
|
|
|
#: src/SUMMARY.md:203
|
|
msgid "Day 4: Afternoon (Android)"
|
|
msgstr "4일차 오후 (안드로이드)"
|
|
|
|
#: src/SUMMARY.md:203
|
|
msgid "Android"
|
|
msgstr "안드로이드"
|
|
|
|
#: src/SUMMARY.md:208
|
|
msgid "Setup"
|
|
msgstr "설치"
|
|
|
|
#: src/SUMMARY.md:209
|
|
msgid "Build Rules"
|
|
msgstr "빌드 규칙"
|
|
|
|
#: src/SUMMARY.md:210
|
|
msgid "Binary"
|
|
msgstr "바이너리"
|
|
|
|
#: src/SUMMARY.md:211
|
|
msgid "Library"
|
|
msgstr "라이브러리"
|
|
|
|
#: src/SUMMARY.md:212
|
|
msgid "AIDL"
|
|
msgstr "AIDL"
|
|
|
|
#: src/SUMMARY.md:213
|
|
msgid "Interface"
|
|
msgstr "AIDL 인터페이스"
|
|
|
|
#: src/SUMMARY.md:214
|
|
msgid "Implementation"
|
|
msgstr "서비스 구현"
|
|
|
|
#: src/SUMMARY.md:215
|
|
msgid "Server"
|
|
msgstr "AIDL 서버"
|
|
|
|
#: src/SUMMARY.md:216
|
|
msgid "Deploy"
|
|
msgstr "배포"
|
|
|
|
#: src/SUMMARY.md:217
|
|
msgid "Client"
|
|
msgstr "클라이언트"
|
|
|
|
#: src/SUMMARY.md:218
|
|
msgid "Changing API"
|
|
msgstr "API 수정"
|
|
|
|
#: src/SUMMARY.md:219
|
|
msgid "Logging"
|
|
msgstr "로깅"
|
|
|
|
#: src/SUMMARY.md:220
|
|
msgid "Interoperability"
|
|
msgstr "상호운용성"
|
|
|
|
#: src/SUMMARY.md:221
|
|
msgid "With C"
|
|
msgstr "C와의 상호운용성"
|
|
|
|
#: src/SUMMARY.md:222
|
|
msgid "Calling C with Bindgen"
|
|
msgstr "Bindgen을 사용한 C호출"
|
|
|
|
#: src/SUMMARY.md:223
|
|
msgid "Calling Rust from C"
|
|
msgstr "C에서 러스트 호출"
|
|
|
|
#: src/SUMMARY.md:224
|
|
msgid "With C++"
|
|
msgstr "C++와의 상호운용성"
|
|
|
|
#: src/SUMMARY.md:225
|
|
msgid "With Java"
|
|
msgstr "Java와의 상호운용성"
|
|
|
|
#: src/SUMMARY.md:228
|
|
msgid "Final Words"
|
|
msgstr "끝으로..."
|
|
|
|
#: src/SUMMARY.md:230
|
|
msgid "Thanks!"
|
|
msgstr "감사인사"
|
|
|
|
#: src/SUMMARY.md:231
|
|
msgid "Other Resources"
|
|
msgstr "러스트 참고 자료"
|
|
|
|
#: src/SUMMARY.md:232
|
|
msgid "Credits"
|
|
msgstr "도와주신 분들"
|
|
|
|
#: src/SUMMARY.md:236
|
|
msgid "Solutions"
|
|
msgstr "해답"
|
|
|
|
#: src/SUMMARY.md:241
|
|
msgid "Day 1 Morning"
|
|
msgstr "1일차 오전"
|
|
|
|
#: src/SUMMARY.md:242
|
|
msgid "Day 1 Afternoon"
|
|
msgstr "1일차 오후"
|
|
|
|
#: src/SUMMARY.md:243
|
|
msgid "Day 2 Morning"
|
|
msgstr "2일차 오전"
|
|
|
|
#: src/SUMMARY.md:244
|
|
msgid "Day 2 Afternoon"
|
|
msgstr "2일차 오후"
|
|
|
|
#: src/SUMMARY.md:245
|
|
msgid "Day 3 Morning"
|
|
msgstr "3일차 오전"
|
|
|
|
#: src/SUMMARY.md:246
|
|
msgid "Day 3 Afternoon"
|
|
msgstr "3일차 오후"
|
|
|
|
#: src/SUMMARY.md:247
|
|
msgid "Day 4 Morning"
|
|
msgstr "4일차 오전"
|
|
|
|
#: src/welcome.md:1
|
|
msgid "# Welcome to Comprehensive Rust 🦀"
|
|
msgstr "# Welcome to Comprehensive Rust 🦀"
|
|
|
|
#: src/welcome.md:3
|
|
msgid "[![Build workflow](https://img.shields.io/github/actions/workflow/status/google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/google/comprehensive-rust/actions/workflows/build.yml)"
|
|
msgstr ""
|
|
|
|
#: src/welcome.md:3
|
|
msgid "Build workflow"
|
|
msgstr ""
|
|
|
|
#: src/welcome.md:3
|
|
msgid ""
|
|
"[![Build workflow](https://img.shields.io/github/actions/workflow/status/google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/google/comprehensive-rust/actions/workflows/build.yml)\n"
|
|
"[![GitHub contributors](https://img.shields.io/github/contributors/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/graphs/contributors)"
|
|
msgstr ""
|
|
"[![Build workflow](https://img.shields.io/github/actions/workflow/status/google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/google/comprehensive-rust/actions/workflows/build.yml)\n"
|
|
"[![GitHub contributors](https://img.shields.io/github/contributors/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/graphs/contributors)"
|
|
|
|
#: src/welcome.md:4
|
|
msgid "GitHub contributors"
|
|
msgstr ""
|
|
|
|
#: src/welcome.md:4
|
|
msgid ""
|
|
"[![GitHub contributors](https://img.shields.io/github/contributors/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/graphs/contributors)\n"
|
|
"[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/stargazers)"
|
|
msgstr ""
|
|
"[![GitHub contributors](https://img.shields.io/github/contributors/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/graphs/contributors)\n"
|
|
"[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/stargazers)"
|
|
|
|
#: src/welcome.md:5
|
|
msgid "GitHub stars"
|
|
msgstr "GitHub stars"
|
|
|
|
#: src/welcome.md:5
|
|
msgid "[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/stargazers)"
|
|
msgstr "[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-rust?style=flat-square)](https://github.com/google/comprehensive-rust/stargazers)"
|
|
|
|
#: src/welcome.md:7
|
|
msgid ""
|
|
"This is a four day Rust course developed by the Android team. The course covers\n"
|
|
"the full spectrum of Rust, from basic syntax to advanced topics like generics\n"
|
|
"and error handling. It also includes Android-specific content on the last day."
|
|
msgstr "이 4일짜리 러스트 강의는 안드로이드 팀이 만들었습니다. 기본 문법부터 제네릭, 에러 핸들링과 같은 고급주제까지 러스트의 모든 것을 포함합니다. 마지막 날에는 안드로이드에 대한 것 까지 다룹니다."
|
|
|
|
#: src/welcome.md:11
|
|
msgid ""
|
|
"The goal of the course is to teach you Rust. We assume you don't know anything\n"
|
|
"about Rust and hope to:"
|
|
msgstr "강의는 당신이 러스트에 대해서 아무것도 모른다고 가정하고 아래의 목표를 가지고 있습니다:"
|
|
|
|
#: src/welcome.md:14
|
|
msgid ""
|
|
"* Give you a comprehensive understanding of the Rust syntax and language.\n"
|
|
"* Enable you to modify existing programs and write new programs in Rust.\n"
|
|
"* Show you common Rust idioms."
|
|
msgstr ""
|
|
"* 러스트 구문과 언어에 대한 포괄적인 이해를 제공합니다.\n"
|
|
"* 기존 프로그램을 수정하고 러스트에서 새 프로그램을 작성할 수 있습니다.\n"
|
|
"* 일반적인 러스트 관용구를 보여줍니다."
|
|
|
|
#: src/welcome.md:18
|
|
msgid "On Day 4, we will cover Android-specific things such as:"
|
|
msgstr "4일차 강의에 우리는 아래와 같은 안드로이드 특화된 내용들도 설명합니다:"
|
|
|
|
#: src/welcome.md:20
|
|
msgid ""
|
|
"* Building Android components in Rust.\n"
|
|
"* AIDL servers and clients.\n"
|
|
"* Interoperability with C, C++, and Java."
|
|
msgstr ""
|
|
"* 러스트에서 Android 구성 요소를 구축.\n"
|
|
"* AIDL 서버 및 클라이언트.\n"
|
|
"* C, C++ 및 Java와의 상호 운용성."
|
|
|
|
#: src/welcome.md:24
|
|
msgid ""
|
|
"It is important to note that this course does not cover Android **application** \n"
|
|
"development in Rust, and that the Android-specific parts are specifically about\n"
|
|
"writing code for Android itself, the operating system. "
|
|
msgstr "이 강의에서는 러스트로 안드로이드 **애플리케이션**을 개발하는 것은 다루지 않습니다. 이 강의에서 다루는 안드로이드 특화된 내용은 안드로이드 OS의 일부를 러스트로 개발하는 것에 대한 것입니다. "
|
|
|
|
#: src/welcome.md:28
|
|
msgid "## Non-Goals"
|
|
msgstr "## 제외사항"
|
|
|
|
#: src/welcome.md:30
|
|
msgid ""
|
|
"Rust is a large language and we won't be able to cover all of it in a few days.\n"
|
|
"Some non-goals of this course are:"
|
|
msgstr "러스트는 며칠만에 모든 것을 다루기에는 너무 큰 언어입니다. 그래서 아래와 같은것을 목표로 하지 않습니다:"
|
|
|
|
#: src/welcome.md:33
|
|
msgid ""
|
|
"* Learn how to use async Rust --- we'll only mention async Rust when\n"
|
|
" covering traditional concurrency primitives. Please see [Asynchronous\n"
|
|
" Programming in Rust](https://rust-lang.github.io/async-book/) instead for\n"
|
|
" details on this topic.\n"
|
|
"* Learn how to develop macros, please see [Chapter 19.5 in the Rust\n"
|
|
" Book](https://doc.rust-lang.org/book/ch19-06-macros.html) and [Rust by\n"
|
|
" Example](https://doc.rust-lang.org/rust-by-example/macros.html) instead."
|
|
msgstr ""
|
|
"* 비동기적 러스트 사용법. 간단하게 언급정도는 하겠지만 좀 더 자세한 내용은 [Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/)를 참조해주세요.\n"
|
|
"* 매크로를 개발하는 방법. [Chapter 19.5 in the Rust Book](https://doc.rust-lang.org/book/ch19-06-macros.html)와 [Rust by Example](https://doc.rust-lang.org/rust-by-example/macros.html)를 참조하세요."
|
|
|
|
#: src/welcome.md:41
|
|
msgid "## Assumptions"
|
|
msgstr "## 독자 수준에 대한 가정"
|
|
|
|
#: src/welcome.md:43
|
|
msgid ""
|
|
"The course assumes that you already know how to program. Rust is a statically\n"
|
|
"typed language and we will sometimes make comparisons with C and C++ to better\n"
|
|
"explain or contrast the Rust approach."
|
|
msgstr "본 강의는 여러분이 프로그래밍 자체에 대해서는 알고 있다고 가정합니다. 러스트는 정적타입 언어이며, 강좌에서는 C/C++ 와의 비교, 대조를 통해 러스트를 설명할 것입니다."
|
|
|
|
#: src/welcome.md:47
|
|
msgid ""
|
|
"If you know how to program in a dynamically typed language such as Python or\n"
|
|
"JavaScript, then you will be able to follow along just fine too."
|
|
msgstr "C/C++을 모르더라도 동적 타입 언어(Python이나 JavaScript 등) 프로그래밍 경험이 있다면 따라오는데 큰 문제는 없을 것입니다."
|
|
|
|
#: src/welcome.md:50 src/cargo/rust-ecosystem.md:19
|
|
#: src/cargo/code-samples.md:22 src/cargo/running-locally.md:68
|
|
#: src/welcome-day-1.md:14 src/welcome-day-1/what-is-rust.md:19
|
|
#: src/hello-world.md:20 src/hello-world/small-example.md:21 src/why-rust.md:9
|
|
#: src/why-rust/compile-time.md:14 src/why-rust/runtime.md:8
|
|
#: src/why-rust/modern.md:19 src/basic-syntax/compound-types.md:28
|
|
#: src/basic-syntax/slices.md:18 src/basic-syntax/string-slices.md:25
|
|
#: src/basic-syntax/functions.md:33 src/basic-syntax/functions-interlude.md:25
|
|
#: src/exercises/day-1/morning.md:9 src/exercises/day-1/for-loops.md:90
|
|
#: src/basic-syntax/variables.md:15 src/basic-syntax/type-inference.md:24
|
|
#: src/basic-syntax/static-and-const.md:46
|
|
#: src/basic-syntax/scopes-shadowing.md:23 src/memory-management/stack.md:26
|
|
#: src/memory-management/rust.md:12 src/ownership/move-semantics.md:20
|
|
#: src/ownership/moves-function-calls.md:18 src/ownership/copy-clone.md:33
|
|
#: src/ownership/borrowing.md:25 src/ownership/shared-unique-borrows.md:23
|
|
#: src/ownership/lifetimes-function-calls.md:27
|
|
#: src/ownership/lifetimes-data-structures.md:23
|
|
#: src/exercises/day-1/afternoon.md:9 src/exercises/day-1/book-library.md:102
|
|
#: src/structs/tuple-structs.md:35 src/structs/field-shorthand.md:25
|
|
#: src/enums.md:31 src/enums/variant-payloads.md:33 src/enums/sizes.md:27
|
|
#: src/methods.md:28 src/methods/receiver.md:23 src/methods/example.md:44
|
|
#: src/pattern-matching.md:23 src/pattern-matching/destructuring-enums.md:33
|
|
#: src/pattern-matching/destructuring-structs.md:21
|
|
#: src/pattern-matching/destructuring-arrays.md:19
|
|
#: src/pattern-matching/match-guards.md:20 src/exercises/day-2/morning.md:9
|
|
#: src/exercises/day-2/points-polygons.md:115 src/control-flow/blocks.md:40
|
|
#: src/control-flow/if-expressions.md:29
|
|
#: src/control-flow/if-let-expressions.md:19
|
|
#: src/control-flow/while-let-expressions.md:25
|
|
#: src/control-flow/for-expressions.md:22
|
|
#: src/control-flow/loop-expressions.md:23
|
|
#: src/control-flow/match-expressions.md:25 src/std.md:23
|
|
#: src/std/option-result.md:16 src/std/string.md:28 src/std/vec.md:35
|
|
#: src/std/hashmap.md:36 src/std/box.md:32 src/std/box-recursive.md:31
|
|
#: src/std/rc.md:29 src/modules.md:26 src/modules/visibility.md:37
|
|
#: src/modules/filesystem.md:24 src/exercises/day-2/afternoon.md:5
|
|
#: src/traits.md:39 src/traits/iterator.md:30 src/traits/from-iterator.md:15
|
|
#: src/traits/from-into.md:27 src/traits/operators.md:24 src/traits/drop.md:32
|
|
#: src/traits/default.md:38 src/generics/methods.md:23
|
|
#: src/generics/trait-bounds.md:33 src/generics/impl-trait.md:22
|
|
#: src/generics/closures.md:23 src/exercises/day-3/morning.md:5
|
|
#: src/error-handling/result.md:25 src/error-handling/try-operator.md:48
|
|
#: src/error-handling/converting-error-types-example.md:48
|
|
#: src/error-handling/deriving-error-enums.md:37
|
|
#: src/error-handling/dynamic-errors.md:34
|
|
#: src/error-handling/error-contexts.md:33 src/unsafe.md:26
|
|
#: src/unsafe/raw-pointers.md:24 src/unsafe/mutable-static-variables.md:30
|
|
#: src/unsafe/unions.md:19 src/unsafe/writing-unsafe-functions.md:31
|
|
#: src/unsafe/extern-functions.md:19 src/unsafe/unsafe-traits.md:28
|
|
#: src/exercises/day-3/afternoon.md:5 src/welcome-day-4.md:6
|
|
#: src/concurrency/threads.md:28 src/concurrency/scoped-threads.md:35
|
|
#: src/concurrency/channels.md:25 src/concurrency/shared_state/arc.md:27
|
|
#: src/concurrency/shared_state/mutex.md:29
|
|
#: src/concurrency/shared_state/example.md:21 src/concurrency/send-sync.md:18
|
|
#: src/concurrency/send-sync/sync.md:12 src/exercises/day-4/morning.md:10
|
|
#: src/android/interoperability/with-c/rust.md:81
|
|
#: src/exercises/day-4/android.md:10
|
|
msgid "<details>"
|
|
msgstr "<details>"
|
|
|
|
#: src/welcome.md:52
|
|
msgid ""
|
|
"This is an example of a _speaker note_. We will use these to add additional\n"
|
|
"information to the slides. This could be key points which the instructor should\n"
|
|
"cover as well as answers to typical questions which come up in class."
|
|
msgstr "이것은 \"발표자 노트\"의 예제입니다. 이 부분을 이용해서 추가 정보를 제공합니다. 주로 강의실에서 제기되는 일반적인 질문에 대한 답변과 강사가 다루어야 할 키 포인트일 수 있습니다."
|
|
|
|
#: src/welcome.md:56 src/cargo/rust-ecosystem.md:67
|
|
#: src/cargo/code-samples.md:35 src/cargo/running-locally.md:74
|
|
#: src/welcome-day-1.md:42 src/welcome-day-1/what-is-rust.md:29
|
|
#: src/hello-world.md:40 src/hello-world/small-example.md:44 src/why-rust.md:24
|
|
#: src/why-rust/compile-time.md:35 src/why-rust/runtime.md:22
|
|
#: src/why-rust/modern.md:66 src/basic-syntax/compound-types.md:62
|
|
#: src/basic-syntax/references.md:28 src/basic-syntax/slices.md:36
|
|
#: src/basic-syntax/string-slices.md:44 src/basic-syntax/functions.md:54
|
|
#: src/exercises/day-1/morning.md:28 src/exercises/day-1/for-loops.md:95
|
|
#: src/basic-syntax/variables.md:20 src/basic-syntax/type-inference.md:48
|
|
#: src/basic-syntax/static-and-const.md:52
|
|
#: src/basic-syntax/scopes-shadowing.md:39 src/memory-management/stack.md:49
|
|
#: src/memory-management/rust.md:18 src/ownership/move-semantics.md:26
|
|
#: src/ownership/moves-function-calls.md:26 src/ownership/copy-clone.md:51
|
|
#: src/ownership/borrowing.md:51 src/ownership/shared-unique-borrows.md:29
|
|
#: src/ownership/lifetimes-function-calls.md:60
|
|
#: src/ownership/lifetimes-data-structures.md:30
|
|
#: src/exercises/day-1/afternoon.md:15 src/exercises/day-1/book-library.md:106
|
|
#: src/structs.md:41 src/structs/tuple-structs.md:43
|
|
#: src/structs/field-shorthand.md:72 src/enums.md:41
|
|
#: src/enums/variant-payloads.md:45 src/enums/sizes.md:155 src/methods.md:41
|
|
#: src/methods/receiver.md:29 src/methods/example.md:53
|
|
#: src/pattern-matching.md:35 src/pattern-matching/destructuring-enums.md:39
|
|
#: src/pattern-matching/destructuring-structs.md:25
|
|
#: src/pattern-matching/destructuring-arrays.md:46
|
|
#: src/pattern-matching/match-guards.md:28 src/exercises/day-2/morning.md:15
|
|
#: src/exercises/day-2/points-polygons.md:125 src/control-flow/blocks.md:46
|
|
#: src/control-flow/if-expressions.md:33
|
|
#: src/control-flow/if-let-expressions.md:26
|
|
#: src/control-flow/while-let-expressions.md:30
|
|
#: src/control-flow/for-expressions.md:29
|
|
#: src/control-flow/loop-expressions.md:27
|
|
#: src/control-flow/match-expressions.md:32 src/std.md:31
|
|
#: src/std/option-result.md:25 src/std/string.md:40 src/std/vec.md:49
|
|
#: src/std/hashmap.md:66 src/std/box.md:39 src/std/box-recursive.md:41
|
|
#: src/std/rc.md:69 src/modules.md:32 src/modules/visibility.md:48
|
|
#: src/modules/filesystem.md:53 src/exercises/day-2/afternoon.md:11
|
|
#: src/traits.md:53 src/traits/iterator.md:39 src/traits/from-iterator.md:26
|
|
#: src/traits/from-into.md:33 src/traits/operators.md:38 src/traits/drop.md:42
|
|
#: src/traits/default.md:47 src/generics/methods.md:31
|
|
#: src/generics/trait-bounds.md:50 src/generics/impl-trait.md:38
|
|
#: src/generics/closures.md:38 src/exercises/day-3/morning.md:11
|
|
#: src/error-handling/result.md:33 src/error-handling/try-operator.md:55
|
|
#: src/error-handling/converting-error-types-example.md:60
|
|
#: src/error-handling/deriving-error-enums.md:45
|
|
#: src/error-handling/dynamic-errors.md:41
|
|
#: src/error-handling/error-contexts.md:42 src/unsafe.md:32
|
|
#: src/unsafe/raw-pointers.md:42 src/unsafe/mutable-static-variables.md:35
|
|
#: src/unsafe/unions.md:28 src/unsafe/writing-unsafe-functions.md:38
|
|
#: src/unsafe/extern-functions.md:28 src/unsafe/unsafe-traits.md:37
|
|
#: src/exercises/day-3/afternoon.md:11 src/welcome-day-4.md:11
|
|
#: src/concurrency/threads.md:45 src/concurrency/scoped-threads.md:40
|
|
#: src/concurrency/channels.md:32 src/concurrency/shared_state/arc.md:38
|
|
#: src/concurrency/shared_state/mutex.md:45
|
|
#: src/concurrency/shared_state/example.md:56 src/concurrency/send-sync.md:23
|
|
#: src/concurrency/send-sync/sync.md:18 src/exercises/day-4/morning.md:16
|
|
#: src/android/interoperability/with-c/rust.md:86
|
|
#: src/exercises/day-4/android.md:15
|
|
msgid "</details>"
|
|
msgstr "</details>"
|
|
|
|
#: src/running-the-course.md:1
|
|
msgid "# Running the Course"
|
|
msgstr "# 강의 진행 방식"
|
|
|
|
#: src/running-the-course.md:3 src/running-the-course/course-structure.md:3
|
|
#: src/running-the-course/day-4.md:3
|
|
msgid "> This page is for the course instructor."
|
|
msgstr "> 강사를 위한 안내 페이지입니다."
|
|
|
|
#: src/running-the-course.md:5
|
|
msgid ""
|
|
"Here is a bit of background information about how we've been running the course\n"
|
|
"internally at Google."
|
|
msgstr "다음은 구글 내부에서 이 과정을 어떤식으로 운영해왔는지에 대한 배경 정보입니다."
|
|
|
|
#: src/running-the-course.md:8
|
|
msgid "Before you run the course, you will want to:"
|
|
msgstr "강의를 실행하기 위한 준비:"
|
|
|
|
#: src/running-the-course.md:10
|
|
msgid ""
|
|
"1. Make yourself familiar with the course material. We've included speaker notes\n"
|
|
" to help highlight the key points (please help us by contributing more speaker\n"
|
|
" notes!). When presenting, you should make sure to open the speaker notes in a\n"
|
|
" popup (click the link with a little arrow next to \"Speaker Notes\"). This way\n"
|
|
" you have a clean screen to present to the class.\n"
|
|
"\n"
|
|
"1. Select your topic for the afternoon of the fourth day. This may be based on\n"
|
|
" the audience you expect, or on your own expertise.\n"
|
|
"\n"
|
|
"1. Decide on the dates. Since the course is large, we recommend that you\n"
|
|
" schedule the four days over two weeks. Course participants have said that\n"
|
|
" they find it helpful to have a gap in the course since it helps them process\n"
|
|
" all the information we give them.\n"
|
|
"\n"
|
|
"1. Find a room large enough for your in-person participants. We recommend a\n"
|
|
" class size of 15-20 people. That's small enough that people are comfortable\n"
|
|
" asking questions --- it's also small enough that one instructor will have\n"
|
|
" time to answer the questions. Make sure the room has _desks_ for yourself and for the\n"
|
|
" students: you will all need to be able to sit and work with your laptops.\n"
|
|
" In particular, you will be doing a lot of live-coding as an instructor, so a lectern won't\n"
|
|
" be very helpful for you.\n"
|
|
"\n"
|
|
"1. On the day of your course, show up to the room a little early to set things\n"
|
|
" up. We recommend presenting directly using `mdbook serve` running on your\n"
|
|
" laptop (see the [installation instructions][3]). This ensures optimal performance with no lag as you change pages.\n"
|
|
" Using your laptop will also allow you to fix typos as you or the course\n"
|
|
" participants spot them.\n"
|
|
"\n"
|
|
"1. Let people solve the exercises by themselves or in small groups. Make sure to\n"
|
|
" ask people if they're stuck or if there is anything you can help with. When\n"
|
|
" you see that several people have the same problem, call it out to the class\n"
|
|
" and offer a solution, e.g., by showing people where to find the relevant\n"
|
|
" information in the standard library.\n"
|
|
"\n"
|
|
"1. Prepare anything you need to have available for the afternoon of day 4."
|
|
msgstr ""
|
|
"1. 강의 자료를 숙지합니다. 주요 요점을 강조하기 위해 강의 참조 노트를 포함하였습니다. (추가적인 노트를 작성하여 제공해 주시면 감사하겠습니다.) 강의 참조 노트의 링크를 누르면 별도의 팝업으로 분리가 되며, 메인 화면에서는 사라집니다. 깔끔한 화면으로 강의를 진행할 수 있습니다.\n"
|
|
"\n"
|
|
"1. 4일차 오후의 주제를 선택합니다. 수강생들이 원하는, 혹은 여러분이 자신있는 주제를 고르세요.\n"
|
|
"\n"
|
|
"1. 강의 날짜를 정합니다. 강의 내용이 많고 수강생들이 모든 정보를 공부할 수 있도록 중간에 틈을 두어 2주에 걸쳐 4일을 잡는 것을 추천합니다.\n"
|
|
"\n"
|
|
"1. 충분한 공간을 확보합니다. 15에서 20명 규모의 공간을 추천합니다. 수강생과 강사가 질의를 하기에 충분한 시간과 공간이어야 합니다. 강사나 수강생 모두 책상을 사용할 수 있는 강의실이면 좋습니다. 강의 중에 강사가 라이브 코딩을 하게 될 경우가 많으며, 이때 자리에 앉아 노트북을 사용하는 것이 도움이 됩니다.\n"
|
|
"\n"
|
|
"1. 강의 당일 조금 일찍 와서 준비합니다. 강사 노트북에서 `mdbook serve -d book/ko`를 이용해 직접 프레젠테이션 하면 페이지 이동 시의 지연이 없습니다.([설치 방법][3]을 참조하세요.) 또한, 그렇게 하면 강의 도중 오타를 발견했을 때 그 자리에서 바로 수정 가능하다는 장점도 있습니다.\n"
|
|
"\n"
|
|
"1. 수강생들이 직접 (개별 혹은 그룹으로) 연습문제를 풀도록 합니다. 진행이 막혀 도움을 필요로 하는 수강생이 없는지 수시로 확인합니다. 만약 같은 문제를 여러 사람이 겪고 있다면, 그 문제를 강의실 전체 인원에게 알리고 해결책을 제시합니다. 예를 들어 표준 라이브러리 어디에 가면 그 문제에 대한 해답을 찾을 수 있는지 알려 줍니다.\n"
|
|
"\n"
|
|
"1. 4일차 오후에 필요한 것들을 준비하세요."
|
|
|
|
#: src/running-the-course.md:46
|
|
msgid ""
|
|
"That is all, good luck running the course! We hope it will be as much fun for\n"
|
|
"you as it has been for us!"
|
|
msgstr "이제 준비는 끝났습니다. 우리가 그랬듯이 여러분들도 이 강의를 즐기시길 바랍니다!"
|
|
|
|
#: src/running-the-course.md:49
|
|
msgid ""
|
|
"Please [provide feedback][1] afterwards so that we can keep improving the\n"
|
|
"course. We would love to hear what worked well for you and what can be made\n"
|
|
"better. Your students are also very welcome to [send us feedback][2]!"
|
|
msgstr "강의를 계속 개선할 수 있도록 [피드백][1]을 제공해 주십시오. 우리는 무엇이 좋았고, 무엇이 모자랐는지 듣고 싶습니다. 수강생들로 부터의 [피드백][2]도 환영합니다!"
|
|
|
|
#: src/running-the-course/course-structure.md:1
|
|
msgid "# Course Structure"
|
|
msgstr "# 강의 구성"
|
|
|
|
#: src/running-the-course/course-structure.md:5
|
|
msgid "The course is fast paced and covers a lot of ground:"
|
|
msgstr "강의는 빠른 속도로 진행되며, 아래 내용들을 다룹니다:"
|
|
|
|
#: src/running-the-course/course-structure.md:7
|
|
msgid ""
|
|
"* Day 1: Basic Rust, ownership and the borrow checker.\n"
|
|
"* Day 2: Compound data types, pattern matching, the standard library.\n"
|
|
"* Day 3: Traits and generics, error handling, testing, unsafe Rust.\n"
|
|
"* Day 4: Concurrency in Rust and seeing Rust in action."
|
|
msgstr ""
|
|
"* 1일차: 러스트 기본, 소유권(ownership)과 빌림(borrow) 체크\n"
|
|
"* 2일차: 복합 데이터 유형, 패턴 매칭, 표준 라이브러리\n"
|
|
"* 3일차: 트레잇(trait)와 제네릭(generic), 오류 처리, 테스트, 안전하지 않은 러스트\n"
|
|
"* 4일차: 러스트의 동시성 및 러스트가 실제 활용되는 사례 살펴보기"
|
|
|
|
#: src/running-the-course/course-structure.md:12
|
|
msgid "## Format"
|
|
msgstr "## 강의 형식"
|
|
|
|
#: src/running-the-course/course-structure.md:14
|
|
msgid ""
|
|
"The course is meant to be very interactive and we recommend letting the\n"
|
|
"questions drive the exploration of Rust!"
|
|
msgstr "이 강의는 강사와 수강생이 양방향으로 소통하면서 진행하도록 디자인 되었습니다. 다양한 질문을 통해 러스트의 여러 부분을 탐험할 수 있도록 하세요!"
|
|
|
|
#: src/running-the-course/day-4.md:1
|
|
msgid "# Day 4"
|
|
msgstr "# 4일차"
|
|
|
|
#: src/running-the-course/day-4.md:5
|
|
msgid ""
|
|
"The afternoon of the fourth day should cover a topic of your choice. Include\n"
|
|
"the topic in the announcement of the course, so that participants know what to\n"
|
|
"expect."
|
|
msgstr "4일차 오후에는 여러분의 선택에 따라 다른 주제를 다룰 수 있습니다. 강의를 안내할 때 4일차 오후의 주제를 포함하여 수강생들이 미리 알 수 있게 하세요."
|
|
|
|
#: src/running-the-course/day-4.md:9
|
|
msgid ""
|
|
"This phase of the course is a chance for participants to see Rust in action on a\n"
|
|
"codebase they might be familiar with. You can choose from the topics already\n"
|
|
"defined here, or plan your own."
|
|
msgstr "4일차 쯤이면, 실제 코드 베이스를 통해 러스트가 활용되는 모습을 살펴보면 좋습니다. 이미 준비된 주제 중에서 고를 수도 있고, 여러분이 직접 준비한 주제를 다룰 수도 있습니다."
|
|
|
|
#: src/running-the-course/day-4.md:13
|
|
msgid "Some topics need additional preparation:"
|
|
msgstr "준비된 주제 (어떤 주제는 추가적인 준비가 필요합니다):"
|
|
|
|
#: src/running-the-course/day-4.md:15
|
|
msgid "## Android"
|
|
msgstr "## 안드로이드"
|
|
|
|
#: src/running-the-course/day-4.md:17
|
|
msgid ""
|
|
"If you chose Android for Day 4 afternoon, you will need an [AOSP checkout][1].\n"
|
|
"Make a checkout of the [course repository][2] on the same machine and move the\n"
|
|
"`src/android/` directory into the root of your AOSP checkout. This will ensure\n"
|
|
"that the Android build system sees the `Android.bp` files in `src/android/`."
|
|
msgstr "4일차 오후에 안드로이드를 다루기로 했다면, [AOSP 코드][1]를 체크아웃해야 합니다. 그런 다음, 같은 컴퓨터에서 [과정 저장소][2]를 체크아웃하고 `src/android/` 디렉터리를 AOSP 코드의 루트로 이동합니다. 이렇게 하면 안드로이드 빌드 시스템에서 과제용으로 추가된 `Android.bp`파일을 인식할 수 있습니다."
|
|
|
|
#: src/running-the-course/day-4.md:22
|
|
msgid ""
|
|
"Ensure that `adb sync` works with your emulator or real device and pre-build\n"
|
|
"all Android examples using `src/android/build_all.sh`. Read the script to see\n"
|
|
"the commands it runs and make sure they work when you run them by hand."
|
|
msgstr "`adb sync` 명렁어가 에뮬레이터 혹은 실제 장치와 작동하는지 확인합니다. 그리고 `src/android/build_all.sh`를 수행해서 모든 안드로이드 예제를 미리 빌드해 보세요. 그 쉘 스크립트를 읽고, 그 안에서 수행되는 명령어들을 확인한 후 각 명령어들을 수동으로 실행해도 잘 되는지 확인하세요."
|
|
|
|
#: src/running-the-course/day-4.md:26
|
|
msgid "## Async"
|
|
msgstr "## 비동기"
|
|
|
|
#: src/running-the-course/day-4.md:28
|
|
msgid ""
|
|
"If you chose Async for Day 4 afternoon, you will need a fresh crate set up and\n"
|
|
"the dependencies downloaded and ready to go. You can then copy/paste the\n"
|
|
"examples into `src/main.rs` to experiment with them."
|
|
msgstr "4일 차 오후에 비동기를 다루기로 했다면, 새 크레이트를 설정하고 몇 가지 의존성을 다운로드해 두어야 합니다. 그런 다음 예제를 `src/main.rs`에 복사/붙여넣기 하여 실험할 수 있습니다."
|
|
|
|
#: src/running-the-course/keyboard-shortcuts.md:1
|
|
msgid "# Keyboard Shortcuts"
|
|
msgstr "# 단축키"
|
|
|
|
#: src/running-the-course/keyboard-shortcuts.md:3
|
|
msgid "There are several useful keyboard shortcuts in mdBook:"
|
|
msgstr "mdBook 시스템(현 사이트)에서 유용한 단축키들 입니다:"
|
|
|
|
#: src/running-the-course/keyboard-shortcuts.md:5
|
|
msgid ""
|
|
"* <kbd>Arrow-Left</kbd>: Navigate to the previous page.\n"
|
|
"* <kbd>Arrow-Right</kbd>: Navigate to the next page.\n"
|
|
"* <kbd>Ctrl + Enter</kbd>: Execute the code sample that has focus.\n"
|
|
"* <kbd>s</kbd>: Activate the search bar."
|
|
msgstr ""
|
|
"* <kbd>왼쪽 화살표</kbd>: 이전 페이지로 이동합니다.\n"
|
|
"* <kbd>오른쪽 화살표</kbd>: 다음 페이지로 이동합니다.\n"
|
|
"* <kbd>Ctrl + Enter</kbd>: 현재 포커스를 받은 코드 샘플 블록을 실행합니다.\n"
|
|
"* <kbd>s</kbd>: 검색창을 활성화합니다.(mdBook 문제로 23.01.19 기준 영어로만 가능합니다.)"
|
|
|
|
#: src/running-the-course/translations.md:1
|
|
msgid "# Translations"
|
|
msgstr "# 다른 언어들"
|
|
|
|
#: src/running-the-course/translations.md:3
|
|
msgid ""
|
|
"The course has been translated into other languages by a set of wonderful\n"
|
|
"volunteers:"
|
|
msgstr "이 과정은 다른 언어로도 제공됩니다. 괄호 안은 번역에 도움 주신 분들입니다."
|
|
|
|
#: src/running-the-course/translations.md:6
|
|
msgid ""
|
|
"* [Brazilian Portuguese][pt-BR] by [@rastringer] and [@hugojacob].\n"
|
|
"* [Korean][ko] by [@keispace], [@jiyongp] and [@jooyunghan]."
|
|
msgstr ""
|
|
"* [영어][en]\n"
|
|
"* [브라질 포르투갈어][pt-BR] ([@rastringer], [@hugojacob]).\n"
|
|
"* [한국어][ko] ([@keispace], [@jiyongp], [@jooyunghan])"
|
|
|
|
#: src/running-the-course/translations.md:9
|
|
msgid "Use the language picker in the top-right corner to switch between languages."
|
|
msgstr "페이지 오른쪽 위의 메뉴를 통해 다른 언어로 전환할 수 있습니다."
|
|
|
|
#: src/running-the-course/translations.md:11
|
|
msgid ""
|
|
"If you want to help with this effort, please see [our instructions] for how to\n"
|
|
"get going. Translations are coordinated on the [issue tracker]."
|
|
msgstr ""
|
|
"이 과정의 번역 작업에 도움을 주고 싶다면 [여기][our instructions] 설명된 내용을 참고하세요.\n"
|
|
"진행 중인 번역 작업에 대한 내용은 [이슈 트래커][issue tracker]를 참고하세요."
|
|
|
|
#: src/cargo.md:1
|
|
msgid "# Using Cargo"
|
|
msgstr "# 카고(Cargo) 사용하기"
|
|
|
|
#: src/cargo.md:3
|
|
msgid ""
|
|
"When you start reading about Rust, you will soon meet [Cargo](https://doc.rust-lang.org/cargo/), the standard tool\n"
|
|
"used in the Rust ecosystem to build and run Rust applications. Here we want to\n"
|
|
"give a brief overview of what Cargo is and how it fits into the wider ecosystem\n"
|
|
"and how it fits into this training."
|
|
msgstr "러스트를 시작하려고하면 당신은 곧 [Cargo](https://doc.rust-lang.org/cargo/)라는, 러스트 생태계에서 사용하는 표준 빌드/실행 도구를 만날 것 입니다. 여기서는 카고가 무엇인지, 그리고 카고가 러스트 생태계에서 어떤 역할을 하는지, 그리고 이 강의에서 어떻게 사용될 지에 대해 간략히 설명하겠습니다."
|
|
|
|
#: src/cargo.md:8
|
|
msgid "## Installation"
|
|
msgstr "## 설치하기"
|
|
|
|
#: src/cargo.md:10
|
|
msgid "### Rustup (Recommended)"
|
|
msgstr "#### Rustup (추천)"
|
|
|
|
#: src/cargo.md:12
|
|
msgid "You can follow the instructions to install cargo and rust compiler, among other standard ecosystem tools with the [rustup][3] tool, which is maintained by the Rust Foundation."
|
|
msgstr "러스트 재단에서 관리하고 있는 [rustup][3] 도구를 사용하여 카고 및 러스트 컴파일러 등 표준 도구를 설치 할 수 있습니다."
|
|
|
|
#: src/cargo.md:14
|
|
msgid "Along with cargo and rustc, Rustup will install itself as a command line utility that you can use to install/switch toolchains, setup cross compilation, etc."
|
|
msgstr "카고(cargo)와 러스트 컴파일러(rustc)와 함께, rustup은 툴체인을 설치하고, 다른 툴체인으로 전환하고, 크로스 컴파일 설정을 하는 일을 담당하는 커맨드 라인 유틸리티 입니다."
|
|
|
|
#: src/cargo.md:16
|
|
msgid "### Package Managers"
|
|
msgstr "### 패키지 매니저"
|
|
|
|
#: src/cargo.md:18
|
|
msgid "#### Debian"
|
|
msgstr "#### 데비안"
|
|
|
|
#: src/cargo.md:20
|
|
msgid "On Debian/Ubuntu, you can install Cargo, the Rust source and the [Rust formatter][6] with"
|
|
msgstr "데비안이나 우분투에서 cargo, 러스트 소스코드, [러스트 포매터][6]를 아래 커맨드로 설치합니다."
|
|
|
|
#: src/cargo.md:22
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ sudo apt install cargo rust-src rustfmt\n"
|
|
"```"
|
|
msgstr ""
|
|
"```shell\n"
|
|
"$ sudo apt install cargo rust-src rustfmt\n"
|
|
"```"
|
|
|
|
#: src/cargo.md:26
|
|
msgid ""
|
|
"This will allow [rust-analyzer][1] to jump to the definitions. We suggest using\n"
|
|
"[VS Code][2] to edit the code (but any LSP compatible editor works)."
|
|
msgstr "이렇게 하면 [rust-analyzer][1]를 이용해서 특정 심볼이 정의된 위치로 쉽게 이동할 수 있습니다. 우리는 에디터로 [VS Code][2]를 추천합니다만, 사실 LSP를 지원한다면 어떤 에디터도 무방합니다."
|
|
|
|
#: src/cargo.md:29
|
|
msgid "Some folks also like to use the [JetBrains][4] family of IDEs, which do their own analysis but have their own tradeoffs. If you prefer them, you can install the [Rust Plugin][5]. Please take note that as of January 2023 debugging only works on the CLion version of the JetBrains IDEA suite."
|
|
msgstr "어떤 사람들은 [JetBrains][4] 제품군을 선호하기도 합니다. 이 제품들은 rust-analyzer 를 활용하지 않고 IDE 자체적으로 구문분석을 합니다. 만약 이 IDE를 설치하셨다면 [Rust Plugin][5]를 설치하시기 바랍니다. 다만 2023년 1월 기준, 디버깅은 JetBrains IDEA suite의 CLion 버전에서만 작동한다는 점에 유의하시기 바랍니다."
|
|
|
|
#: src/cargo/rust-ecosystem.md:1
|
|
msgid "# The Rust Ecosystem"
|
|
msgstr "# 러스트 생태계"
|
|
|
|
#: src/cargo/rust-ecosystem.md:3
|
|
msgid "The Rust ecosystem consists of a number of tools, of which the main ones are:"
|
|
msgstr "러스트의 생태계는 여러가지 도구들로 구성되어 있으며, 그 중 중요한 것들은 아래와 같습니다:"
|
|
|
|
#: src/cargo/rust-ecosystem.md:5
|
|
msgid ""
|
|
"* `rustc`: the Rust compiler which turns `.rs` files into binaries and other\n"
|
|
" intermediate formats.\n"
|
|
"\n"
|
|
"* `cargo`: the Rust dependency manager and build tool. Cargo knows how to\n"
|
|
" download dependencies hosted on <https://crates.io> and it will pass them to\n"
|
|
" `rustc` when building your project. Cargo also comes with a built-in test\n"
|
|
" runner which is used to execute unit tests.\n"
|
|
"\n"
|
|
"* `rustup`: the Rust toolchain installer and updater. This tool is used to\n"
|
|
" install and update `rustc` and `cargo` when new versions of Rust is released.\n"
|
|
" In addition, `rustup` can also download documentation for the standard\n"
|
|
" library. You can have multiple versions of Rust installed at once and `rustup`\n"
|
|
" will let you switch between them as needed."
|
|
msgstr ""
|
|
"* `rustc`: `.rs` 확장자 파일을 바이너리 혹은 다른 중간 형식으로 변환해주는 Rust 컴파일러입니다.\n"
|
|
"\n"
|
|
"* `cargo`: 러스트 의존성 관리자 및 빌드도구 입니다. 여러분의 프로젝트에 명시된 의존성들을 <https://crates.io>에서 자동으로 다운로드 받고, 그 소스코드를 `rustc`로 전달하여 빌드를 시킵니다. 또한 유닛 테스트를 실행하는 테스트 러너를 내장하고 있습니다.\n"
|
|
"\n"
|
|
"* `rustup`: 러스트 툴체인 설치 프로그램 및 업데이트 프로그램. 이 도구는 새 버전의 러스트가 출시될 때 `rustc` 및 `cargo` 설치하고 업데이트하는 데 사용됩니다. 또한 `rustup`은 표준 라이브러리에 대한 문서를 다운로드할 수도 있습니다. 한 번에 여러 버전의 러스트를 설치할 수 있으며 `rustup`을 통해 필요에 따라 이들 버전을 전환할 수 있습니다."
|
|
|
|
#: src/cargo/rust-ecosystem.md:21 src/hello-world.md:25
|
|
#: src/hello-world/small-example.md:27 src/why-rust/runtime.md:10
|
|
#: src/why-rust/modern.md:21 src/basic-syntax/compound-types.md:30
|
|
#: src/pattern-matching/destructuring-enums.md:35
|
|
#: src/error-handling/try-operator.md:50
|
|
#: src/error-handling/converting-error-types-example.md:50
|
|
#: src/concurrency/threads.md:30
|
|
msgid "Key points:"
|
|
msgstr "키 포인트:"
|
|
|
|
#: src/cargo/rust-ecosystem.md:23
|
|
msgid ""
|
|
"* Rust has a rapid release schedule with a new release coming out\n"
|
|
" every six weeks. New releases maintain backwards compatibility with\n"
|
|
" old releases --- plus they enable new functionality.\n"
|
|
"\n"
|
|
"* There are three release channels: \"stable\", \"beta\", and \"nightly\".\n"
|
|
"\n"
|
|
"* New features are being tested on \"nightly\", \"beta\" is what becomes\n"
|
|
" \"stable\" every six weeks.\n"
|
|
"\n"
|
|
"* Rust also has [editions]: the current edition is Rust 2021. Previous\n"
|
|
" editions were Rust 2015 and Rust 2018.\n"
|
|
"\n"
|
|
" * The editions are allowed to make backwards incompatible changes to\n"
|
|
" the language.\n"
|
|
"\n"
|
|
" * To prevent breaking code, editions are opt-in: you select the\n"
|
|
" edition for your crate via the `Cargo.toml` file.\n"
|
|
"\n"
|
|
" * To avoid splitting the ecosystem, Rust compilers can mix code\n"
|
|
" written for different editions.\n"
|
|
"\n"
|
|
" * Mention that it is quite rare to ever use the compiler directly not through `cargo` (most users never do).\n"
|
|
"\n"
|
|
" * It might be worth alluding that Cargo itself is an extremely powerful and comprehensive tool. It is capable of many advanced features including but not limited to: \n"
|
|
" * Project/package structure\n"
|
|
" * [workspaces]\n"
|
|
" * Dev Dependencies and Runtime Dependency management/caching\n"
|
|
" * [build scripting]\n"
|
|
" * [global installation]\n"
|
|
" * It is also extensible with sub command plugins as well (such as [cargo clippy]).\n"
|
|
" * Read more from the [official Cargo Book]"
|
|
msgstr ""
|
|
"* 러스트는 6주마다 새로운 릴리즈가 발표되며 이전 릴리즈와의 호환성을 유지하고 있습니다.\n"
|
|
"\n"
|
|
"* 릴리즈는 3가지 버전으로 제공됩니다: \"stable\", \"beta\" 그리고 \"nightly\".\n"
|
|
"\n"
|
|
"* 새로운 기능은 \"nightly\" -> \"beta\" -(6주 후)-> \"stable\" 로 변경됩니다.\n"
|
|
"\n"
|
|
"* 러스트는 [에디션][editions]으로 구분됩니다. 현재는 Rust 2021 에디션입니다. 이 전 에디션으로 Rust 2015와 Rust 2018이 있습니다.\n"
|
|
"\n"
|
|
" * 에디션은 이전 에디션과 호환이 되지 않을 수 있습니다.\n"
|
|
"\n"
|
|
" * 에디션이 바뀌면서 프로그램이 의도치 않게 깨지는 문제를 막기 위해, 각 프로그램은 자신이 빌드될 에디션을 명시적으로 `Cargo.toml`에 지정해야 합니다.\n"
|
|
"\n"
|
|
" * 러스트 생태계가 에디션 별로 파편회 되는 것을 막기 위해, 러스트 컴파일러는 서로 다른 에디션에서 작성된 코드들을 하나의 바이너리로 묶을 수 있습니다.\n"
|
|
"\n"
|
|
" * `cargo`를 사용하지 않고 컴파일러를 직접 사용하는 경우는 거의 없음을 언급해 주시기 바랍니다.\n"
|
|
"\n"
|
|
" * 카고 자체가 매우 강력하고 포괄적인 도구임을 적극적으로 알리세요.\n"
|
|
" 카고는 다음과 같은 다양한 고급 기능을 제공합니다.\n"
|
|
" * 프로젝트/패키지 구조화\n"
|
|
" * [워크스페이스][workspaces]\n"
|
|
" * 개발/런타임 종속성 관리 및 캐싱\n"
|
|
" * [빌드 스크립트][build scripting]\n"
|
|
" * [전역 설치][global installation]\n"
|
|
" * [cargo clippy]와 같은 하위 플러그인으로 확장 가능\n"
|
|
" * [공식 Cargo Book][official Cargo Book]에서 자세한 사항을 확인하시기 바랍니다."
|
|
|
|
#: src/cargo/code-samples.md:1
|
|
msgid "# Code Samples in This Training"
|
|
msgstr "# 강의에서의 코드 샘플"
|
|
|
|
#: src/cargo/code-samples.md:3
|
|
msgid ""
|
|
"For this training, we will mostly explore the Rust language through examples\n"
|
|
"which can be executed through your browser. This makes the setup much easier and\n"
|
|
"ensures a consistent experience for everyone."
|
|
msgstr "이 강의자료에 있는 모든 예제는 브라우저에서 바로 수행 가능합니다. 이렇게 한 이유는, 준비 과정을 단순화 시키고, 모두가 같은 환경에서 작업할 수 있도록 하기 위함입니다."
|
|
|
|
#: src/cargo/code-samples.md:7
|
|
msgid ""
|
|
"Installing Cargo is still encouraged: it will make it easier for you to do the\n"
|
|
"exercises. On the last day, we will do a larger exercise which shows you how to\n"
|
|
"work with dependencies and for that you need Cargo."
|
|
msgstr "그럼에도 불구하고, 카고(cargo)를 직접 설치하는 것을 강력 권장합니다. 이게 과제 작성에 더 도움이 될겁니다. 또한, 마지막 날에는 의존성이 있는 예제를 작업하게 될 텐데, 그 때에는 어차피 카고가 필요합니다."
|
|
|
|
#: src/cargo/code-samples.md:11
|
|
msgid "The code blocks in this course are fully interactive:"
|
|
msgstr "이 강의 자료의 코드 블록들은 전부 인터엑티브 합니다:"
|
|
|
|
#: src/cargo/code-samples.md:13
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" println!(\"Edit me!\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" println!(\"Edit me!\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/cargo/code-samples.md:19
|
|
msgid ""
|
|
"You can use <kbd>Ctrl + Enter</kbd> to execute the code when focus is in the\n"
|
|
"text box."
|
|
msgstr "코드 블록에 포커스를 두고 <kbd>Ctrl + Enter</kbd>를 눌러 실행해 볼 수 있습니다."
|
|
|
|
#: src/cargo/code-samples.md:24
|
|
msgid ""
|
|
"Most code samples are editable like shown above. A few code samples\n"
|
|
"are not editable for various reasons:"
|
|
msgstr "강의에서 대부분의 코드 샘플은 위와 같이 수정할수 있지만 일부 코드는 다음과 같은 이유로 수정할 수 없습니다:"
|
|
|
|
#: src/cargo/code-samples.md:27
|
|
msgid ""
|
|
"* The embedded playgrounds cannot execute unit tests. Copy-paste the\n"
|
|
" code and open it in the real Playground to demonstrate unit tests.\n"
|
|
"\n"
|
|
"* The embedded playgrounds lose their state the moment you navigate\n"
|
|
" away from the page! This is the reason that the students should\n"
|
|
" solve the exercises using a local Rust installation or via the\n"
|
|
" Playground."
|
|
msgstr ""
|
|
"* 유닛 테스트는 내장 플레이그라운드에서 실행이 안됩니다. 외부 플레이그라운드 사이트에 붙여넣어 테스트를 실행하시기 바랍니다.\n"
|
|
"\n"
|
|
"* 내장된 플레이그라운드에서는 페이지 이동시 작성된 모든 내용이 사라집니다. 따라서 로컬 환경이나 외부 플레이그라운드 사이트에서 연습문제를 해결하는 것이 좋습니다."
|
|
|
|
#: src/cargo/running-locally.md:1
|
|
msgid "# Running Code Locally with Cargo"
|
|
msgstr "# 로컬 환경의 카고"
|
|
|
|
#: src/cargo/running-locally.md:3
|
|
msgid ""
|
|
"If you want to experiment with the code on your own system, then you will need\n"
|
|
"to first install Rust. Do this by following the [instructions in the Rust\n"
|
|
"Book][1]. This should give you a working `rustc` and `cargo`. At the time of\n"
|
|
"writing, the latest stable Rust release has these version numbers:"
|
|
msgstr "만약 개인용 컴퓨터에서 코드를 실행해보려면 먼저 러스트를 설치해야 합니다. [Rust Book][1]의 지침에 따라 `rustc`와 `cargo`를 함께 설치 하시기 바랍니다. 설치 후 아래 커맨드를 통해 각 툴의 버전을 확인 할 수 있습니다:"
|
|
|
|
#: src/cargo/running-locally.md:8
|
|
msgid ""
|
|
"```shell\n"
|
|
"% rustc --version\n"
|
|
"rustc 1.61.0 (fe5b13d68 2022-05-18)\n"
|
|
"% cargo --version\n"
|
|
"cargo 1.61.0 (a028ae4 2022-04-29)\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/cargo/running-locally.md:15
|
|
msgid ""
|
|
"With this is in place, then follow these steps to build a Rust binary from one\n"
|
|
"of the examples in this training:"
|
|
msgstr "정상적으로 설치가 되었으면 강의의 코드 블록중 하나를 아래 단계를 따라 로컬에서 실행할 수 있습니다:"
|
|
|
|
#: src/cargo/running-locally.md:18
|
|
msgid ""
|
|
"1. Click the \"Copy to clipboard\" button on the example you want to copy.\n"
|
|
"\n"
|
|
"2. Use `cargo new exercise` to create a new `exercise/` directory for your code:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cargo new exercise\n"
|
|
" Created binary (application) `exercise` package\n"
|
|
" ```\n"
|
|
"\n"
|
|
"3. Navigate into `exercise/` and use `cargo run` to build and run your binary:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cd exercise\n"
|
|
" $ cargo run\n"
|
|
" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n"
|
|
" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n"
|
|
" Running `target/debug/exercise`\n"
|
|
" Hello, world!\n"
|
|
" ```\n"
|
|
"\n"
|
|
"4. Replace the boiler-plate code in `src/main.rs` with your own code. For\n"
|
|
" example, using the example on the previous page, make `src/main.rs` look like\n"
|
|
"\n"
|
|
" ```rust\n"
|
|
" fn main() {\n"
|
|
" println!(\"Edit me!\");\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
"5. Use `cargo run` to build and run your updated binary:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cargo run\n"
|
|
" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n"
|
|
" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n"
|
|
" Running `target/debug/exercise`\n"
|
|
" Edit me!\n"
|
|
" ```\n"
|
|
"\n"
|
|
"6. Use `cargo check` to quickly check your project for errors, use `cargo build`\n"
|
|
" to compile it without running it. You will find the output in `target/debug/`\n"
|
|
" for a normal debug build. Use `cargo build --release` to produce an optimized\n"
|
|
" release build in `target/release/`.\n"
|
|
"\n"
|
|
"7. You can add dependencies for your project by editing `Cargo.toml`. When you\n"
|
|
" run `cargo` commands, it will automatically download and compile missing\n"
|
|
" dependencies for you."
|
|
msgstr ""
|
|
"1. 예시 블록에 있는 \"Copy to clipboard\" 버튼을 클릭해서 복사합니다.\n"
|
|
"\n"
|
|
"2. 터미널에서 `cargo new exercise`를 입력해서 새로운 `exercise/` 폴더를 만듭니다:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cargo new exercise\n"
|
|
" Created binary (application) `exercise` package\n"
|
|
" ```\n"
|
|
"\n"
|
|
"3. `exercise/` 폴더로 이동한 후, `cargo run` 커맨드로 코드를 실행합니다:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cd exercise\n"
|
|
" $ cargo run\n"
|
|
" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n"
|
|
" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n"
|
|
" Running `target/debug/exercise`\n"
|
|
" Hello, world!\n"
|
|
" ```\n"
|
|
"\n"
|
|
"4. `src/main.rs`에 코드를 작성합니다. 예를 들어 이전 페이지의 소스를 아래와 같이 `src/main.rs`에 작성합니다\n"
|
|
"\n"
|
|
" ```rust\n"
|
|
" fn main() {\n"
|
|
" println!(\"Edit me!\");\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
"5. `cargo run`커맨드로 소스를 빌드하고 실행합니다:\n"
|
|
"\n"
|
|
" ```shell\n"
|
|
" $ cargo run\n"
|
|
" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n"
|
|
" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n"
|
|
" Running `target/debug/exercise`\n"
|
|
" Edit me!\n"
|
|
" ```\n"
|
|
"\n"
|
|
"6. `cargo check`커맨드는 빠르게 에러를 확인할 수 있습니다. `cargo build`는 실행없이 컴파일만 합니다. 이 경우에 `target/debug/`폴더에서 output을 확인 할 수 있습니다. `cargo build --release`커맨드는 릴리즈 버전용 최적화를 켜서 컴파일하며 `target/release/`폴더에서 확인 할 수 있습니다.\n"
|
|
"\n"
|
|
"7. `Cargo.toml`파일에는 의존성 패키지를 추가할 수 있습니다. `cargo`커맨드를 실행하면 자동으로 의존성 패키지를 다운로드하고 컴파일 까지 해 줍니다."
|
|
|
|
#: src/cargo/running-locally.md:70
|
|
msgid ""
|
|
"Try to encourage the class participants to install Cargo and use a\n"
|
|
"local editor. It will make their life easier since they will have a\n"
|
|
"normal development environment."
|
|
msgstr "수강생들이 카고를 설치하고 로컬 편집기를 이용하도록 독려하세요. 조금 귀찮을 수도 있지만, 이렇게 해야만 좀 더 실제와 가까운 개발환경을 갖추게 되는 것입니다."
|
|
|
|
#: src/welcome-day-1.md:1
|
|
msgid "# Welcome to Day 1"
|
|
msgstr "# 1일차 개요"
|
|
|
|
#: src/welcome-day-1.md:3
|
|
msgid ""
|
|
"This is the first day of Comprehensive Rust. We will cover a lot of ground\n"
|
|
"today:"
|
|
msgstr "강의 첫 날입니다. 오늘 배울 것이 참 많습니다:"
|
|
|
|
#: src/welcome-day-1.md:6
|
|
msgid ""
|
|
"* Basic Rust syntax: variables, scalar and compound types, enums, structs,\n"
|
|
" references, functions, and methods.\n"
|
|
"\n"
|
|
"* Memory management: stack vs heap, manual memory management, scope-based memory\n"
|
|
" management, and garbage collection.\n"
|
|
"\n"
|
|
"* Ownership: move semantics, copying and cloning, borrowing, and lifetimes."
|
|
msgstr ""
|
|
"* 러스트 기본 문법: 변수, 스칼라 / 복합 타입, 열거형, 구조체, 참조형, 함수와 메서드.\n"
|
|
"\n"
|
|
"* 메모리 관리: 스택과 힙, 수동 메모리 관리, 스코프(범위)기반 메모리 관리, 가비지 컬렉션(GC)\n"
|
|
"\n"
|
|
"* 소유권: Move 문법, 복사와 복제, 빌림, 수명."
|
|
|
|
#: src/welcome-day-1.md:16
|
|
msgid "Please remind the students that:"
|
|
msgstr "학생들에게 다음을 상기시켜 주시기 바랍니다:"
|
|
|
|
#: src/welcome-day-1.md:18
|
|
msgid ""
|
|
"* They should ask questions when they get them, don't save them to the end.\n"
|
|
"* The class is meant to be interactive and discussions are very much encouraged!\n"
|
|
" * As an instructor, you should try to keep the discussions relevant, i.e.,\n"
|
|
" keep the related to how Rust does things vs some other language. It can be\n"
|
|
" hard to find the right balance, but err on the side of allowing discussions\n"
|
|
" since they engage people much more than one-way communication.\n"
|
|
"* The questions will likely mean that we talk about things ahead of the slides.\n"
|
|
" * This is perfectly okay! Repetition is an important part of learning. Remember\n"
|
|
" that the slides are just a support and you are free to skip them as you\n"
|
|
" like."
|
|
msgstr ""
|
|
"* 궁금한 점이 있으면 주저하지 말고 질문 해야 합니다.\n"
|
|
"* 이 수업은 상호작용이 중요하고 토론을 권장합니다.\n"
|
|
" * 강사로서 토론이 옆길로 새지 않게 주의하세요. 예를 들어 러스트와 다른 언어들을 비교한다든지 하는 것은 좋습니다. 적절한 균형을 찾기 애매한 경우라면 토론을 허용하는 쪽이 일방적인 강의보다는 더 많은 사람들의 참여를 이끌어 낼 수 있습니다.\n"
|
|
"* 질문이 슬라이드보다 앞서가도 괜찮습니다.\n"
|
|
" * 학습에 있어서 반복은 매우 중요합니다. 슬라이드는 그저 도움을 줄 뿐, 원하는 대로 건너띄어도 됩니다."
|
|
|
|
#: src/welcome-day-1.md:29
|
|
msgid ""
|
|
"The idea for the first day is to show _just enough_ of Rust to be able to speak\n"
|
|
"about the famous borrow checker. The way Rust handles memory is a major feature\n"
|
|
"and we should show students this right away."
|
|
msgstr "첫 날 강의의 목표는, 러스트에서 그 유명한 빌림 확인에 대해서 이야기 할 수 있을 정도 까지만 러스트를 소개하는 것입니다. 러스트의 가장 독특한 특징이 메모리를 다루는 방식이기 때문에, 학생들에게 이 부분 을 우선적으로 보여주려 합니다."
|
|
|
|
#: src/welcome-day-1.md:33
|
|
msgid ""
|
|
"If you're teaching this in a classroom, this is a good place to go over the\n"
|
|
"schedule. We suggest splitting the day into two parts (following the slides):"
|
|
msgstr "만약 당신이 강의실에서 가르치고 있다면, 이 슬라이드는 일정을 검토하기에 적합한 곳입니다. 하루치 강의를 아래처럼 오전 오후로 나누어 진행하는 것을 추천합니다. (슬라이드가 그런식으로 나뉘어 있습니다.)"
|
|
|
|
#: src/welcome-day-1.md:36
|
|
msgid ""
|
|
"* Morning: 9:00 to 12:00,\n"
|
|
"* Afternoon: 13:00 to 16:00."
|
|
msgstr ""
|
|
"* 오전: 9:00 ~ 12:00,\n"
|
|
"* 오후: 13:00 ~ 16:00."
|
|
|
|
#: src/welcome-day-1.md:39
|
|
msgid ""
|
|
"You can of course adjust this as necessary. Please make sure to include breaks,\n"
|
|
"we recommend a break every hour!"
|
|
msgstr "물론 필요에 따라 조절할 수 있습니다. 강의 중간에 쉬는시간을 넣는 것을 잊지 마세요. 매 시간 휴식을 갖는걸 추천합니다!"
|
|
|
|
#: src/welcome-day-1/what-is-rust.md:1
|
|
msgid "# What is Rust?"
|
|
msgstr "# 러스트란?"
|
|
|
|
#: src/welcome-day-1/what-is-rust.md:3
|
|
msgid "Rust is a new programming language which had its [1.0 release in 2015][1]:"
|
|
msgstr "러스트는 2015년에 [버전 1.0][1]을 릴리즈 한 새로운 프로그램 언어입니다:"
|
|
|
|
#: src/welcome-day-1/what-is-rust.md:5
|
|
msgid ""
|
|
"* Rust is a statically compiled language in a similar role as C++\n"
|
|
" * `rustc` uses LLVM as its backend.\n"
|
|
"* Rust supports many [platforms and\n"
|
|
" architectures](https://doc.rust-lang.org/nightly/rustc/platform-support.html):\n"
|
|
" * x86, ARM, WebAssembly, ...\n"
|
|
" * Linux, Mac, Windows, ...\n"
|
|
"* Rust is used for a wide range of devices:\n"
|
|
" * firmware and boot loaders,\n"
|
|
" * smart displays,\n"
|
|
" * mobile phones,\n"
|
|
" * desktops,\n"
|
|
" * servers."
|
|
msgstr ""
|
|
"* 러스트는 C++와 유사한 정적 컴파일 언어입니다.\n"
|
|
" * `rustc`는 LLVM을 백엔드로 사용합니다.\n"
|
|
"* 러스트는 다양한 플랫폼과 아키텍쳐를 지원합니다.\n"
|
|
" * x86, ARM, WebAssembly, ...\n"
|
|
" * Linux, Mac, Windows, ...\n"
|
|
"* 러스트는 다양한 장치에서 사용될 수 있습니다:\n"
|
|
" * 펌웨어와 부트로더(임베디드)\n"
|
|
" * 스마트 디스플레이\n"
|
|
" * 스마트폰\n"
|
|
" * 데스크탑\n"
|
|
" * 서버"
|
|
|
|
#: src/welcome-day-1/what-is-rust.md:21
|
|
msgid "Rust fits in the same area as C++:"
|
|
msgstr "러스트는 C++가 사용되는 대부분의 곳에서 사용 가능합니다:"
|
|
|
|
#: src/welcome-day-1/what-is-rust.md:23
|
|
msgid ""
|
|
"* High flexibility.\n"
|
|
"* High level of control.\n"
|
|
"* Can be scaled down to very constrained devices like mobile phones.\n"
|
|
"* Has no runtime or garbage collection.\n"
|
|
"* Focuses on reliability and safety without sacrificing performance."
|
|
msgstr ""
|
|
"* 높은 유연성.\n"
|
|
"* 높은 레벨 수준의 제어.\n"
|
|
"* 휴대폰과 같은 매우 제한된 장치로 스케일 다운 가능.\n"
|
|
"* 별도의 런타임을 필요로 하지 않으며, 가비지 컬렉션도 없음.\n"
|
|
"* 성능을 타협하지 않으면서도 안정성과 안전에 중점을 둠."
|
|
|
|
#: src/hello-world.md:1
|
|
msgid "# Hello World!"
|
|
msgstr "# Hello World!"
|
|
|
|
#: src/hello-world.md:3
|
|
msgid ""
|
|
"Let us jump into the simplest possible Rust program, a classic Hello World\n"
|
|
"program:"
|
|
msgstr "가장 간단한 러스트 프로그램으로써, 고전적인 Hello World 를 작성해 보겠습니다:"
|
|
|
|
#: src/hello-world.md:6
|
|
msgid ""
|
|
"```rust\n"
|
|
"fn main() {\n"
|
|
" println!(\"Hello 🌍!\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/hello-world.md:12
|
|
msgid "What you see:"
|
|
msgstr "확인할 수 있는 것들:"
|
|
|
|
#: src/hello-world.md:14
|
|
msgid ""
|
|
"* Functions are introduced with `fn`.\n"
|
|
"* Blocks are delimited by curly braces like in C and C++.\n"
|
|
"* The `main` function is the entry point of the program.\n"
|
|
"* Rust has hygienic macros, `println!` is an example of this.\n"
|
|
"* Rust strings are UTF-8 encoded and can contain any Unicode character."
|
|
msgstr ""
|
|
"* 함수는 `fn`으로 선언합니다.\n"
|
|
"* C/C++ 와 마찬가지로 중괄호`{}`로 블록을 표시합니다.\n"
|
|
"* `main` 함수는 프로그램 진입점입니다.\n"
|
|
"* 러스트는 똑똑한 매크로(hygienic macros) 시스템을 가지고 있습니다. `println!`는 그 예시입니다.\n"
|
|
"* 러스트의 문자열은 UTF-8로 인코딩되며 이모지와 같은 유니코드 문자를 포함할 수 있습니다."
|
|
|
|
#: src/hello-world.md:22
|
|
msgid ""
|
|
"This slide tries to make the students comfortable with Rust code. They will see\n"
|
|
"a ton of it over the next four days so we start small with something familiar."
|
|
msgstr "이 슬라이드는 학생들이 러스트 코드에 익숙해지기 위해 작성되었습니다. 앞으로 4일 동안 많은 코드를 접할 것이기 때문에 우선 친숙한 코드부터 시작합니다."
|
|
|
|
#: src/hello-world.md:27
|
|
msgid ""
|
|
"* Rust is very much like other languages in the C/C++/Java tradition. It is\n"
|
|
" imperative (not functional) and it doesn't try to reinvent things unless\n"
|
|
" absolutely necessary.\n"
|
|
"\n"
|
|
"* Rust is modern with full support for things like Unicode.\n"
|
|
"\n"
|
|
"* Rust uses macros for situations where you want to have a variable number of\n"
|
|
" arguments (no function [overloading](basic-syntax/functions-interlude.md)).\n"
|
|
"\n"
|
|
"* Macros being 'hygienic' means they don't accidentally capture identifiers from\n"
|
|
" the scope they are used in. Rust macros are actually only\n"
|
|
" [partially hygenic](https://veykril.github.io/tlborm/decl-macros/minutiae/hygiene.html)."
|
|
msgstr ""
|
|
"* 러스트는 C/C++/Java와 같은 전통적인 다른 언어와 매우 유사합니다. 러스트는 절차적(함수형 아님) 언어입니다. 정말로 필요한 경우가 아니라면, 러스트는 이미 존재하는 것을 새로 구현하려고 하지 않습니다.\n"
|
|
"\n"
|
|
"* 러스트는 유니코드 지원과 같은 현대 언어의 특징을 전부 지원합니다.\n"
|
|
"\n"
|
|
"* 러스트는 인자의 개수를 사전에 지정할 수 없는 상황에서 함수 [오버로딩](basic-syntax/functions-interlude.md)대신 매크로를 사용합니다.\n"
|
|
"\n"
|
|
"* 똑똑한 매크로(hygienic macro)는 매크로가 사용되는 스코프에서 의도치 않게 변수를 가로채지 않습니다. 사실 러스트 매크로는 완전히 hygenic하지는 않습니다. [링크](https://veykril.github.io/tlborm/decl-macros/minutiae/hygiene.html)를 참고하세요."
|
|
|
|
#: src/hello-world/small-example.md:1
|
|
msgid "# Small Example"
|
|
msgstr "# 작은 예제"
|
|
|
|
#: src/hello-world/small-example.md:3
|
|
msgid "Here is a small example program in Rust:"
|
|
msgstr "러스트로 작성된 작은 예제입니다:"
|
|
|
|
#: src/hello-world/small-example.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() { // Program entry point\n"
|
|
" let mut x: i32 = 6; // Mutable variable binding\n"
|
|
" print!(\"{x}\"); // Macro for printing, like printf\n"
|
|
" while x != 1 { // No parenthesis around expression\n"
|
|
" if x % 2 == 0 { // Math like in other languages\n"
|
|
" x = x / 2;\n"
|
|
" } else {\n"
|
|
" x = 3 * x + 1;\n"
|
|
" }\n"
|
|
" print!(\" -> {x}\");\n"
|
|
" }\n"
|
|
" println!();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() { // 프로그램 진입점입니다.\n"
|
|
" let mut x: i32 = 6; // 가변 변수 할당(binding)입니다.\n"
|
|
" print!(\"{x}\"); // printf와 같은 출력을 위한 매크로 입니다.\n"
|
|
" while x != 1 { // 표현식에 괄호는 없습니다.\n"
|
|
" if x % 2 == 0 { // 다른 언어와 같은 수학연산식이 사용됩니다.\n"
|
|
" x = x / 2;\n"
|
|
" } else {\n"
|
|
" x = 3 * x + 1;\n"
|
|
" }\n"
|
|
" print!(\" -> {x}\");\n"
|
|
" }\n"
|
|
" println!();\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/hello-world/small-example.md:23
|
|
msgid ""
|
|
"The code implements the Collatz conjecture: it is believed that the loop will\n"
|
|
"always end, but this is not yet proved. Edit the code and play with different\n"
|
|
"inputs."
|
|
msgstr "이 코드는 콜라츠 추측(Collatz conjecture)으로 구현됩니다: 반복문이 언제나 종료될 것이라고 믿지만 증명된 것은 아닙니다. 코드를 수정하고 실행해 보시기 바랍니다."
|
|
|
|
#: src/hello-world/small-example.md:29
|
|
msgid ""
|
|
"* Explain that all variables are statically typed. Try removing `i32` to trigger\n"
|
|
" type inference. Try with `i8` instead and trigger a runtime integer overflow.\n"
|
|
"\n"
|
|
"* Change `let mut x` to `let x`, discuss the compiler error.\n"
|
|
"\n"
|
|
"* Show how `print!` gives a compilation error if the arguments don't match the\n"
|
|
" format string.\n"
|
|
"\n"
|
|
"* Show how you need to use `{}` as a placeholder if you want to print an\n"
|
|
" expression which is more complex than just a single variable.\n"
|
|
"\n"
|
|
"* Show the students the standard library, show them how to search for `std::fmt`\n"
|
|
" which has the rules of the formatting mini-language. It's important that the\n"
|
|
" students become familiar with searching in the standard library."
|
|
msgstr ""
|
|
"* 모든 변수가 컴파일 시 정해진 타입을 가짐을 설명합니다. `i32`를 삭제하여 컴파일러가 타입 추론을 하도록 해 봅니다. `i32`을 `i8`로 변경하여 런타임 오버플로를 유발해 볼 수 있습니다.\n"
|
|
"\n"
|
|
"* `let mut x`를 `let x`로 수정하여 컴파일 오류에 대해 토론합니다.\n"
|
|
"\n"
|
|
"* 인자가 포맷 문자열과 일치하지 않는 경우 `print!`에서 컴파일 오류가 발생함을 언급하는 것도 좋습니다.\n"
|
|
"\n"
|
|
"* 단일 변수보다 복잡한 식을 출려하려면 `{}`을 자리 표시자로 사용하는 방법을 보여 줍니다.\n"
|
|
"\n"
|
|
"* 학생들에게 표준 라이브러리가 어디 있는지 알려 주고는, `print!`가 지원하는 포맷팅 언어의 문법을 알기 위해 `std::fmt`를 검색해야 한다는 것을 가르치세요.학생들이 표준 라이브러리의 검색 기능에 익숙해 지도록 하는 것이 중요합니다."
|
|
|
|
#: src/why-rust.md:1
|
|
msgid "# Why Rust?"
|
|
msgstr "# 러스트를 써야하는 이유"
|
|
|
|
#: src/why-rust.md:3
|
|
msgid "Some unique selling points of Rust:"
|
|
msgstr "러스트만의 독특한 세일즈 포인트(장점):"
|
|
|
|
#: src/why-rust.md:5
|
|
msgid ""
|
|
"* Compile time memory safety.\n"
|
|
"* Lack of undefined runtime behavior.\n"
|
|
"* Modern language features."
|
|
msgstr ""
|
|
"* 컴파일 시 메모리 안전이 보장됨.\n"
|
|
"* 정의되지 않은 런타임 동작이 없음.\n"
|
|
"* 현대적인 언어 기능."
|
|
|
|
#: src/why-rust.md:11
|
|
msgid ""
|
|
"Make sure to ask the class which languages they have experience with. Depending\n"
|
|
"on the answer you can highlight different features of Rust:"
|
|
msgstr "수강생들에게 어떤 프로그래밍 언어를 사용했는지 물어보시기 바랍니다. 어떤 언어를 사용했느냐에 따라 러스트에서 어떤 점을 강조해야 할지를 고민해 보세요:"
|
|
|
|
#: src/why-rust.md:14
|
|
msgid ""
|
|
"* Experience with C or C++: Rust eliminates a whole class of _runtime errors_\n"
|
|
" via the borrow checker. You get performance like in C and C++, but you don't\n"
|
|
" have the memory unsafety issues. In addition, you get a modern language with\n"
|
|
" constructs like pattern matching and built-in dependency management.\n"
|
|
"\n"
|
|
"* Experience with Java, Go, Python, JavaScript...: You get the same memory safety\n"
|
|
" as in those languages, plus a similar high-level language feeling. In addition\n"
|
|
" you get fast and predictable performance like C and C++ (no garbage collector)\n"
|
|
" as well as access to low-level hardware (should you need it)"
|
|
msgstr ""
|
|
"* C/C++: 러스트는 '빌림'검사기를 통해서 수행중에 발생할 수 있는 모든 에러를 제거합니다. 러스트는 C와 C++과 비슷한 수준의 성능을 보여주면서도, 그 언어들에서 종종 발생하는 메모리 관련 오류가 없습니다. 또한, 패턴 매칭이나, 기본적으로 제공되는 종속성 관리와 같은 현대적인 언어의 기능들을 제공합니다.\n"
|
|
"\n"
|
|
"* Java, Go, Python, JaveScript: 이 언어들과 동일한 메모리 안정성과 함께, '하이레벨'언어의 느낌을 느낄 수 있습니다. 거기에 더해, 가비지 컬렉터가 없는 C/C++와 유사한 수준의 빠르고 예측 가능한 성능을 기대할 수 있습니다. 그리고 필요한 경우 저수준 하드웨어를 다루는 코드로 작성할 수 있습니다."
|
|
|
|
#: src/why-rust/compile-time.md:1
|
|
msgid "# Compile Time Guarantees"
|
|
msgstr "# 컴파일 시 보장되는 것들"
|
|
|
|
#: src/why-rust/compile-time.md:3
|
|
msgid "Static memory management at compile time:"
|
|
msgstr "컴파일 시 정적 메모리 관리:"
|
|
|
|
#: src/why-rust/compile-time.md:5
|
|
msgid ""
|
|
"* No uninitialized variables.\n"
|
|
"* No memory leaks (_mostly_, see notes).\n"
|
|
"* No double-frees.\n"
|
|
"* No use-after-free.\n"
|
|
"* No `NULL` pointers.\n"
|
|
"* No forgotten locked mutexes.\n"
|
|
"* No data races between threads.\n"
|
|
"* No iterator invalidation."
|
|
msgstr ""
|
|
"* 초기화되지 않는 변수가 없습니다.\n"
|
|
"* 메모리 누출 없음(_거의_. 강의참조노트 참고.)\n"
|
|
"* 메모리 이중 해제가 원천적으로 불가능 합니다.\n"
|
|
"* 메모리 해제 후 사용이 원천적으로 불가능 합니다.\n"
|
|
"* `NULL`포인터는 없습니다.\n"
|
|
"* 뮤텍스를 잠궈 놓고 여는 것을 잊는 실수를 할 수 없습니다.\n"
|
|
"* 스레드간 데이터 레이스를 막아줍니다.\n"
|
|
"* 반복자가 갑자기 무효화 되는 경우가 없습니다."
|
|
|
|
#: src/why-rust/compile-time.md:16
|
|
msgid ""
|
|
"It is possible to produce memory leaks in (safe) Rust. Some examples\n"
|
|
"are:"
|
|
msgstr "(안전한) 러스트에서도 메모리 누출이 발생할 수 있는 몇 가지 경우가 있습니다:"
|
|
|
|
#: src/why-rust/compile-time.md:19
|
|
msgid ""
|
|
"* You can for use [`Box::leak`] to leak a pointer. A use of this could\n"
|
|
" be to get runtime-initialized and runtime-sized static variables\n"
|
|
"* You can use [`std::mem::forget`] to make the compiler \"forget\" about\n"
|
|
" a value (meaning the destructor is never run).\n"
|
|
"* You can also accidentally create a [reference cycle] with `Rc` or\n"
|
|
" `Arc`.\n"
|
|
"* In fact, some will consider infinitely populating a collection a memory\n"
|
|
" leak and Rust does not protect from those."
|
|
msgstr ""
|
|
"* [`Box::leak`]을 이용하여 포인터를 의도적으로 누출시킬 수 있습니다. 이를 이용해서 런타임이 생성하고 런타임이 크기를 정한 정적 변수를 가져올 수 있습니다.\n"
|
|
"* [`std::mem::forget`]을 사용하여 컴파일러가 값에 대해 \"잊도록\" 만들 수 있습니다(소멸자가 실행되지 않음을 의미합니다).\n"
|
|
"* `Rc` 또는 `Arc`를 사용하여 실수로 [순환참조][reference cycle]를 생성할 수도 있습니다.\n"
|
|
"* 컬렉션을 무한정 채우는 것을 메모리 누출로 간주할 수도 있지만, 러스트는 이를 보호하진 못합니다."
|
|
|
|
#: src/why-rust/compile-time.md:28
|
|
msgid ""
|
|
"For the purpose of this course, \"No memory leaks\" should be understood\n"
|
|
"as \"Pretty much no *accidental* memory leaks\"."
|
|
msgstr "본 강의에서는 \"메모리 누출 없음\"을 \"우발적인 메모리 누출 없음\"으로 이해해야 합니다."
|
|
|
|
#: src/why-rust/runtime.md:1
|
|
msgid "# Runtime Guarantees"
|
|
msgstr "# 런타임 시 보장되는 것들"
|
|
|
|
#: src/why-rust/runtime.md:3
|
|
msgid "No undefined behavior at runtime:"
|
|
msgstr "런타임 시 정의되지 않음(undefined) 동작 없음:"
|
|
|
|
#: src/why-rust/runtime.md:5
|
|
msgid ""
|
|
"* Array access is bounds checked.\n"
|
|
"* Integer overflow is defined."
|
|
msgstr ""
|
|
"* 배열 접근시 경계 체크\n"
|
|
"* 정수형 타입의 변수에서 오버플로우 발생시 동작이 잘 정의되어있습니다."
|
|
|
|
#: src/why-rust/runtime.md:12
|
|
msgid ""
|
|
"* Integer overflow is defined via a compile-time flag. The options are\n"
|
|
" either a panic (a controlled crash of the program) or wrap-around\n"
|
|
" semantics. By default, you get panics in debug mode (`cargo build`)\n"
|
|
" and wrap-around in release mode (`cargo build --release`).\n"
|
|
"\n"
|
|
"* Bounds checking cannot be disabled with a compiler flag. It can also\n"
|
|
" not be disabled directly with the `unsafe` keyword. However,\n"
|
|
" `unsafe` allows you to call functions such as `slice::get_unchecked`\n"
|
|
" which does not do bounds checking."
|
|
msgstr ""
|
|
"* 정수형 오버플로우는 컴파일 타임 플레그를 통해 정의됩니다. 옵션은 패닉(프로그램 크레시) 혹은 올림(wrap-around)입니다. 기본적으로 디버그 모드(`cargo build`)에서는 패닉이, 릴리즈 모드(`cargo build --release`)에서는 wrap-around가 발생합니다.\n"
|
|
"\n"
|
|
"* 컴파일 플래그를 사용하여 경계체크를 무력화 할 수 없습니다. `unsafe`를 사용하더라도 마찬가지입니다. 하지만 `unsafe`에서 호출 가능한 `slice::get_unchecked`같은 함수는 경계 검사를 수행하지 않습니다."
|
|
|
|
#: src/why-rust/modern.md:1
|
|
msgid "# Modern Features"
|
|
msgstr "# 현대적인 특징"
|
|
|
|
#: src/why-rust/modern.md:3
|
|
msgid "Rust is built with all the experience gained in the last 40 years."
|
|
msgstr "러스트는 지난 40년간의 모든 (프로그래밍 언어들의) 경험으로 만들어졌습니다."
|
|
|
|
#: src/why-rust/modern.md:5
|
|
msgid "## Language Features"
|
|
msgstr "## 언어적 특징"
|
|
|
|
#: src/why-rust/modern.md:7
|
|
msgid ""
|
|
"* Enums and pattern matching.\n"
|
|
"* Generics.\n"
|
|
"* No overhead FFI.\n"
|
|
"* Zero-cost abstractions."
|
|
msgstr ""
|
|
"* 열거형과 패턴 매칭.\n"
|
|
"* 제네릭.\n"
|
|
"* FFI 런타임 오버헤드 없음.\n"
|
|
" * _역주: FFI: Foreign Function Interface. 타 언어 코드를 호출하기 위한 인터페이스_\n"
|
|
"* 제로 코스트 추상화."
|
|
|
|
#: src/why-rust/modern.md:12
|
|
msgid "## Tooling"
|
|
msgstr "## 도구들"
|
|
|
|
#: src/why-rust/modern.md:14
|
|
msgid ""
|
|
"* Great compiler errors.\n"
|
|
"* Built-in dependency manager.\n"
|
|
"* Built-in support for testing.\n"
|
|
"* Excellent Language Server Protocol support."
|
|
msgstr ""
|
|
"* 친절한 컴파일러 오류메시지.\n"
|
|
"* 내장 종속성 관리자.\n"
|
|
"* 내장 테스트 지원.\n"
|
|
"* LSP (Language Server Protocol, 언어 서버 프로토콜) 지원이 잘되어 있음."
|
|
|
|
#: src/why-rust/modern.md:23
|
|
msgid ""
|
|
"* Zero-cost abstractions, similar to C++, means that you don't have to 'pay'\n"
|
|
" for higher-level programming constructs with memory or CPU. For example,\n"
|
|
" writing a loop using `for` should result in roughly the same low level\n"
|
|
" instructions as using the `.iter().fold()` construct.\n"
|
|
"\n"
|
|
"* It may be worth mentioning that Rust enums are 'Algebraic Data Types', also\n"
|
|
" known as 'sum types', which allow the type system to express things like\n"
|
|
" `Option<T>` and `Result<T, E>`.\n"
|
|
"\n"
|
|
"* Remind people to read the errors --- many developers have gotten used to\n"
|
|
" ignore lengthy compiler output. The Rust compiler is significantly more\n"
|
|
" talkative than other compilers. It will often provide you with _actionable_\n"
|
|
" feedback, ready to copy-paste into your code.\n"
|
|
"\n"
|
|
"* The Rust standard library is small compared to languages like Java, Python,\n"
|
|
" and Go. Rust does not come with several things you might consider standard and\n"
|
|
" essential:\n"
|
|
"\n"
|
|
" * a random number generator, but see [rand].\n"
|
|
" * support for SSL or TLS, but see [rusttls].\n"
|
|
" * support for JSON, but see [serde_json].\n"
|
|
"\n"
|
|
" The reasoning behind this is that functionality in the standard library cannot\n"
|
|
" go away, so it has to be very stable. For the examples above, the Rust\n"
|
|
" community is still working on finding the best solution --- and perhaps there\n"
|
|
" isn't a single \"best solution\" for some of these things.\n"
|
|
"\n"
|
|
" Rust comes with a built-in package manager in the form of Cargo and this makes\n"
|
|
" it trivial to download and compile third-party crates. A consequence of this\n"
|
|
" is that the standard library can be smaller.\n"
|
|
"\n"
|
|
" Discovering good third-party crates can be a problem. Sites like\n"
|
|
" <https://lib.rs/> help with this by letting you compare health metrics for\n"
|
|
" crates to find a good and trusted one.\n"
|
|
" \n"
|
|
"* [rust-analyzer] is a well supported LSP implementation used in major\n"
|
|
" IDEs and text editors."
|
|
msgstr ""
|
|
"* C++ 와 유사하게 제로 코스트 추상화는 CPU나 메모리를 사용하여 상위레벨 프로그래밍 구조를 만드는데 '비용'을 지불할 필요가 없습니다. 예를 들어 `for` 루프와와 `iter().fold()` 구조를 사용하는 것과 거의 동일한 낮은 수준의 명령어가 생성될 것 입니다.\n"
|
|
"\n"
|
|
"* 러스트의 열거형(enum)은 합계 타입(sum type)으로 알려진 대수학적 데이터형(Algebraic Data Type)으로, 타입 시스템이 `Option<T>`와 `Result<T, E>`등을 표현할 수 있게 해줍니다.\n"
|
|
"\n"
|
|
"* 오류를 읽어보시기 바랍니다 --- 오랜기간 많은 개발자들이 컴파일러 출력을 무시하는데 익숙해져 있습니다. 러스트 컴파일러는 다른 컴파일러보다 더 수다스럽고, 복사-붙여넣기 할 수 있는 정도의 코드 피드백을 제공하는 경우가 많습니다.\n"
|
|
"\n"
|
|
"* 러스트 표준 라이브러리는 Java, Python이나 Go와 같은 언어에 비해서 규모가 작습니다. 당연히 포함되어야 한다고 생각할 수도 있는 아래와 같은 것들이 러스트의 표준 라이브러리에 없습니다:\n"
|
|
"\n"
|
|
" * 난수 생성기, 하지만 [rand]문서를 참조하시기 바랍니다.\n"
|
|
" * SSL 또는 TLS지원, 하지만 [rusttls]문서를 참조하시기 바랍니다.\n"
|
|
" * JSON 지원, 하지만 [serde_json] 문서를 참조하시기 바랍니다.\n"
|
|
"\n"
|
|
" 그 이유는 표준 라이브러리에서 한 번 어떤 기능을 제공하면 뺄 수 없으며, 매우 안정적이어야 하기 때문입니다. 위에 언급한 기능들은 아직 러스트 커뮤니티가 최고의 솔루션을 찾지 못했기 때문에 표준 라이브러리에 포함되지 않았습니다. 어쩌면 이들 중 몇 개는 '최고의 솔루션'이 아예 존재할 수 없을 지도 모릅니다.\n"
|
|
"\n"
|
|
" 러스트는 카고라는 패키지 관리자가 내장되어 있고, 서드파티 크레이트를 다운로드, 컴파일 하기 매우 쉽습니다. 이 또한 표준 라이브러리가 작은 이유입니다.\n"
|
|
"\n"
|
|
" 좋은 서드파티 크레이트를 찾는 것은 어렵습니다. <https://lib.rs> 와 같은 사이트가 신뢰할수 있는 좋은 크레이트를 비교하여 찾는데 좋습니다.\n"
|
|
"\n"
|
|
"* [rust-analyzer]는 주요 IDE나 텍스트 에디터에서 사용되는 러스트용 LSP서버 입니다."
|
|
|
|
#: src/basic-syntax.md:1
|
|
msgid "# Basic Syntax"
|
|
msgstr "# 기본 문법"
|
|
|
|
#: src/basic-syntax.md:3
|
|
msgid "Much of the Rust syntax will be familiar to you from C, C++ or Java:"
|
|
msgstr "대부분의 러스트 문법은 C/C++/Java 와 유사합니다:"
|
|
|
|
#: src/basic-syntax.md:5
|
|
msgid ""
|
|
"* Blocks and scopes are delimited by curly braces.\n"
|
|
"* Line comments are started with `//`, block comments are delimited by `/* ...\n"
|
|
" */`.\n"
|
|
"* Keywords like `if` and `while` work the same.\n"
|
|
"* Variable assignment is done with `=`, comparison is done with `==`."
|
|
msgstr ""
|
|
"* 블록과 범위는 중괄호`{}`로 표현합니다.\n"
|
|
"* 인라인 주석은 `//`, 블록 주석은 `/* ... */`로 사용합니다.\n"
|
|
"* `if`나 `while`같은 키워드도 동일합니다.\n"
|
|
"* 변수 할당은 `=`, 비교는 `==`를 사용합니다."
|
|
|
|
#: src/basic-syntax/scalar-types.md:1
|
|
msgid "# Scalar Types"
|
|
msgstr "# 스칼라 타입"
|
|
|
|
#: src/basic-syntax/scalar-types.md:3
|
|
msgid ""
|
|
"| | Types | Literals |\n"
|
|
"|------------------------|--------------------------------------------|-------------------------------|\n"
|
|
"| Signed integers | `i8`, `i16`, `i32`, `i64`, `i128`, `isize` | `-10`, `0`, `1_000`, `123i64` |\n"
|
|
"| Unsigned integers | `u8`, `u16`, `u32`, `u64`, `u128`, `usize` | `0`, `123`, `10u16` |\n"
|
|
"| Floating point numbers | `f32`, `f64` | `3.14`, `-10.0e20`, `2f32` |\n"
|
|
"| Strings | `&str` | `\"foo\"`, `r#\"\\\\\"#` |\n"
|
|
"| Unicode scalar values | `char` | `'a'`, `'α'`, `'∞'` |\n"
|
|
"| Byte strings | `&[u8]` | `b\"abc\"`, `br#\" \" \"#` |\n"
|
|
"| Booleans | `bool` | `true`, `false` |"
|
|
msgstr ""
|
|
"| | 타입 | 스칼라 리터럴 값 |\n"
|
|
"|-----------------|--------------------------------------------|-------------------------------|\n"
|
|
"| 부호있는 정수 | `i8`, `i16`, `i32`, `i64`, `i128`, `isize` | `-10`, `0`, `1_000`, `123i64` |\n"
|
|
"| 부호없는 정수 | `u8`, `u16`, `u32`, `u64`, `u128`, `usize` | `0`, `123`, `10u16` |\n"
|
|
"| 부동소수 | `f32`, `f64` | `3.14`, `-10.0e20`, `2f32` |\n"
|
|
"| 문자열 | `&str` | `\"foo\"`, `r#\"\\\\\"#` |\n"
|
|
"| 유니코드 문자 | `char` | `'a'`, `'α'`, `'∞'` |\n"
|
|
"| 바이트 문자 | `&[u8]` | `b\"abc\"`, `br#\" \" \"#` |\n"
|
|
"| 불리언 | `bool` | `true`, `false` |"
|
|
|
|
#: src/basic-syntax/scalar-types.md:13
|
|
msgid "The types have widths as follows:"
|
|
msgstr "각 타입의 크기는 다음과 같습니다:"
|
|
|
|
#: src/basic-syntax/scalar-types.md:15
|
|
msgid ""
|
|
"* `iN`, `uN`, and `fN` are _N_ bits wide,\n"
|
|
"* `isize` and `usize` are the width of a pointer,\n"
|
|
"* `char` is 32 bit wide,\n"
|
|
"* `bool` is 8 bit wide."
|
|
msgstr ""
|
|
"* 정수(`i`) 및 부동소수형(`f`)은 뒤의 숫자와 같은 비트 수 입니다. (`i8` = 8비트)\n"
|
|
"* `isize` 와 `usize` 는 포인터와 같은 크기입니다.\n"
|
|
" * _역주: 32 비트 시스템에서는 32 비트, 64 비트 시스템에서는 64 비트. C의 `int`와 같음._\n"
|
|
"* `char` 32 비트 입니다.\n"
|
|
"* `bool`은 8 비트 입니다."
|
|
|
|
#: src/basic-syntax/compound-types.md:1
|
|
msgid "# Compound Types"
|
|
msgstr "# 복합 타입"
|
|
|
|
#: src/basic-syntax/compound-types.md:3
|
|
msgid ""
|
|
"| | Types | Literals |\n"
|
|
"|--------|-------------------------------|-----------------------------------|\n"
|
|
"| Arrays | `[T; N]` | `[20, 30, 40]`, `[0; 3]` |\n"
|
|
"| Tuples | `()`, `(T,)`, `(T1, T2)`, ... | `()`, `('x',)`, `('x', 1.2)`, ... |"
|
|
msgstr ""
|
|
"| | 타입 | 리터럴 |\n"
|
|
"|------|-------------------------------|-----------------------------------|\n"
|
|
"| 배열 | `[T; N]` | `[20, 30, 40]`, `[0; 3]` |\n"
|
|
"| 튜플 | `()`, `(T,)`, `(T1, T2)`, ... | `()`, `('x',)`, `('x', 1.2)`, ... |"
|
|
|
|
#: src/basic-syntax/compound-types.md:8
|
|
msgid "Array assignment and access:"
|
|
msgstr "배열 선언과 접근:"
|
|
|
|
#: src/basic-syntax/compound-types.md:10
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut a: [i8; 10] = [42; 10];\n"
|
|
" a[5] = 0;\n"
|
|
" println!(\"a: {:?}\", a);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/compound-types.md:18
|
|
msgid "Tuple assignment and access:"
|
|
msgstr "튜플 선언과 접근:"
|
|
|
|
#: src/basic-syntax/compound-types.md:20
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let t: (i8, bool) = (7, true);\n"
|
|
" println!(\"1st index: {}\", t.0);\n"
|
|
" println!(\"2nd index: {}\", t.1);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/compound-types.md:32
|
|
msgid "Arrays:"
|
|
msgstr "배열:"
|
|
|
|
#: src/basic-syntax/compound-types.md:34
|
|
msgid ""
|
|
"* Arrays have elements of the same type, `T`, and length, `N`, which is a compile-time constant.\n"
|
|
" Note that the length of the array is *part of its type*, which means that `[u8; 3]` and\n"
|
|
" `[u8; 4]` are considered two different types.\n"
|
|
"\n"
|
|
"* We can use literals to assign values to arrays.\n"
|
|
"\n"
|
|
"* In the main function, the print statement asks for the debug implementation with the `?` format\n"
|
|
" parameter: `{}` gives the default output, `{:?}` gives the debug output. We\n"
|
|
" could also have used `{a}` and `{a:?}` without specifying the value after the\n"
|
|
" format string.\n"
|
|
"\n"
|
|
"* Adding `#`, eg `{a:#?}`, invokes a \"pretty printing\" format, which can be easier to read."
|
|
msgstr ""
|
|
"* 배열은, 같은 타입 `T`의 값이 `N`개 있는 것입니다. 여기서 `N`은 컴파일 타임에 결정된 값이어야 합니다. 이 길이도 타입의 일부입니다. 따라서, `[u8; 3]`와 `[u8; 4]`은 서로 다른 타입입니다.\n"
|
|
"\n"
|
|
"* 리터럴을 사용하여 배열에 값을 할당할 수 있습니다.\n"
|
|
"\n"
|
|
"* 포매팅 문자열에서 `?`는 디버깅 출력을 의미합니다. `{}`는 기본 출력이며, `{:?}`는 디버깅 출력입니다. `{a}`, `{a:?}`와 같이 출력할 변수 이름을 포매팅 문자열에 포함시킬 수도 있으며, 이 경우 인자 `a`는 별도의 인자로 추가하지 않습니다.\n"
|
|
"\n"
|
|
"* `#`을 추가하면(`{a:#?}`) 좀 더 읽기 쉬운 \\\"이쁜\\\" 형태로 출력이 됩니다."
|
|
|
|
#: src/basic-syntax/compound-types.md:47
|
|
msgid "Tuples:"
|
|
msgstr "튜플:"
|
|
|
|
#: src/basic-syntax/compound-types.md:49
|
|
msgid ""
|
|
"* Like arrays, tuples have a fixed length.\n"
|
|
"\n"
|
|
"* Tuples group together values of different types into a compound type.\n"
|
|
"\n"
|
|
"* Fields of a tuple can be accessed by the period and the index of the value, e.g. `t.0`, `t.1`.\n"
|
|
"\n"
|
|
"* The empty tuple `()` is also known as the \"unit type\". It is both a type, and\n"
|
|
" the only valid value of that type - that is to say both the type and its value\n"
|
|
" are expressed as `()`. It is used to indicate, for example, that a function or\n"
|
|
" expression has no return value, as we'll see in a future slide. \n"
|
|
" * You can think of it as `void` that can be familiar to you from other \n"
|
|
" programming languages."
|
|
msgstr ""
|
|
"* 배열과 마찬가지로 튜플은 고정 길이를 갖습니다.\n"
|
|
"\n"
|
|
"* 튜플은 서로 다른 타입의 값들을 하나의 복합 타입으로 묶습니다.\n"
|
|
"\n"
|
|
"* 튜플에 속한 값은 `t.0`, `t.1`과 같이 인덱스로 접근할 수 있습니다.\n"
|
|
"\n"
|
|
"* 비어있는 튜플`()`은 단위 타입(unit type)이라고도 합니다. 이는 타입이면서 해당 타입의 유일하며 유효한 값입니다. 즉 타입과 값이 모두 `()`입니다. 예를 들어 함수나 식에서 반환 값이 없음을 나타낼 때 사용합니다.\n"
|
|
" * 다른 언어에서 익숙한 `void` 개념으로 생각할 수 있습니다."
|
|
|
|
#: src/basic-syntax/references.md:1
|
|
msgid "# References"
|
|
msgstr "# 참조"
|
|
|
|
#: src/basic-syntax/references.md:3
|
|
msgid "Like C++, Rust has references:"
|
|
msgstr "C++와 마찬가지로 러스트도 참조형을 갖습니다:"
|
|
|
|
#: src/basic-syntax/references.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut x: i32 = 10;\n"
|
|
" let ref_x: &mut i32 = &mut x;\n"
|
|
" *ref_x = 20;\n"
|
|
" println!(\"x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/references.md:14
|
|
msgid "Some notes:"
|
|
msgstr "참고사항:"
|
|
|
|
#: src/basic-syntax/references.md:16
|
|
msgid ""
|
|
"* We must dereference `ref_x` when assigning to it, similar to C and C++ pointers.\n"
|
|
"* Rust will auto-dereference in some cases, in particular when invoking\n"
|
|
" methods (try `ref_x.count_ones()`).\n"
|
|
"* References that are declared as `mut` can be bound to different values over their lifetime."
|
|
msgstr ""
|
|
"* `ref_x`에 값을 할당할 때, C/C++의 포인터와 유사하게 `*`를 이용해서 참조를 따라가야(역참조) 합니다.\n"
|
|
"* 러스트는 특정한 경우(메서드 호출)에 자동으로 역참조를 합니다.(`ref_x.count_one()`을 하면 `*ref_x`가 `count_one`의 인자로 전달됩니다.)\n"
|
|
"* `mut`로 선언된 참조는 그 변수가 살아있는 동안 다른 값을 가질 수 있습니다."
|
|
|
|
#: src/basic-syntax/references.md:21
|
|
msgid ""
|
|
"<details>\n"
|
|
"Key points:"
|
|
msgstr ""
|
|
"<details>\n"
|
|
"키 포인트:"
|
|
|
|
#: src/basic-syntax/references.md:24
|
|
msgid ""
|
|
"* Be sure to note the difference between `let mut ref_x: &i32` and `let ref_x:\n"
|
|
" &mut i32`. The first one represents a mutable reference which can be bound to\n"
|
|
" different values, while the second represents a reference to a mutable value."
|
|
msgstr "* `let mut ref_x: &i32`와 `let ref_x: &mut i32`의 차이점에 주의 하시기 바랍니다. 첫번째 값은 다른 값에 바인딩 될 수 있는 가변 참조이고, 두번째 값은 가변 값에 대한 참조입니다."
|
|
|
|
#: src/basic-syntax/references-dangling.md:1
|
|
msgid "# Dangling References"
|
|
msgstr "# 허상(dangling) 참조"
|
|
|
|
#: src/basic-syntax/references-dangling.md:3
|
|
msgid "Rust will statically forbid dangling references:"
|
|
msgstr "러스트는 허상(dangling) 참조를 컴파일러 단계에서 찾아내고 금지합니다:"
|
|
|
|
#: src/basic-syntax/references-dangling.md:5
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let ref_x: &i32;\n"
|
|
" {\n"
|
|
" let x: i32 = 10;\n"
|
|
" ref_x = &x;\n"
|
|
" }\n"
|
|
" println!(\"ref_x: {ref_x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/references-dangling.md:16
|
|
msgid ""
|
|
"* A reference is said to \"borrow\" the value it refers to.\n"
|
|
"* Rust is tracking the lifetimes of all references to ensure they live long\n"
|
|
" enough.\n"
|
|
"* We will talk more about borrowing when we get to ownership."
|
|
msgstr ""
|
|
"* 참조는 어떤 값을 \"빌리는\" 것입니다.\n"
|
|
"* 러스트는 참조 대상의 값이, 그 값에 대한 모든 참조들보다 더 오래 살아있음을 추적합니다.\n"
|
|
"* 소유권에 대한 주제를 다룰 때 이 빌림에 대해 더 자세히 이야기 하겠습니다."
|
|
|
|
#: src/basic-syntax/slices.md:1
|
|
msgid "# Slices"
|
|
msgstr "# 슬라이스"
|
|
|
|
#: src/basic-syntax/slices.md:3
|
|
msgid "A slice gives you a view into a larger collection:"
|
|
msgstr "슬라이스는 큰 컬랙션의 일부(혹은 전체)를 보여주는 뷰(view)입니다:"
|
|
|
|
#: src/basic-syntax/slices.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let a: [i32; 6] = [10, 20, 30, 40, 50, 60];\n"
|
|
" println!(\"a: {a:?}\");\n"
|
|
"\n"
|
|
" let s: &[i32] = &a[2..4];\n"
|
|
" println!(\"s: {s:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let a: [i32; 6] = [10, 20, 30, 40, 50, 60];\n"
|
|
" println!(\"a: {a:?}\");\n"
|
|
"\n"
|
|
" let s: &[i32] = &a[2..4];\n"
|
|
" println!(\"s: {s:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/basic-syntax/slices.md:15
|
|
msgid ""
|
|
"* Slices borrow data from the sliced type.\n"
|
|
"* Question: What happens if you modify `a[3]`?"
|
|
msgstr ""
|
|
"* 슬라이스는 다른(슬라이스 된) 타입으로부터 데이터를 '빌려'옵니다.\n"
|
|
"* 질문: `a[3]`을 수정하면 무슨 일이 있어날까요?"
|
|
|
|
#: src/basic-syntax/slices.md:20
|
|
msgid ""
|
|
"* We create a slice by borrowing `a` and specifying the starting and ending indexes in brackets.\n"
|
|
"\n"
|
|
"* If the slice starts at index 0, Rust’s range syntax allows us to drop the starting index, meaning that `&a[0..a.len()]` and `&a[..a.len()]` are identical.\n"
|
|
" \n"
|
|
"* The same is true for the last index, so `&a[2..a.len()]` and `&a[2..]` are identical.\n"
|
|
"\n"
|
|
"* To easily create a slice of the full array, we can therefore use `&a[..]`.\n"
|
|
"\n"
|
|
"* `s` is a reference to a slice of `i32`s. Notice that the type of `s` (`&[i32]`) no longer mentions the array length. This allows us to perform computation on slices of different sizes.\n"
|
|
" \n"
|
|
"* Slices always borrow from another object. In this example, `a` has to remain 'alive' (in scope) for at least as long as our slice. \n"
|
|
" \n"
|
|
"* The question about modifying `a[3]` can spark an interesting discussion, but the answer is that for memory safety reasons\n"
|
|
" you cannot do it through `a` after you created a slice, but you can read the data from both `a` and `s` safely. \n"
|
|
" More details will be explained in the borrow checker section."
|
|
msgstr ""
|
|
"* 슬라이스는 우선 `a`를 빌린다음, 시작과 끝 인덱스를 브래킷(`[]`)안에 지정해서 만듭니다.\n"
|
|
"\n"
|
|
"* 슬라이스가 인덱스 0부터 시작한다면 시작 인덱스는 생략 가능합니다. 즉 `&a[0..a.len()]`와 `&a[..a.len()]` 는 동일합니다.\n"
|
|
"\n"
|
|
"* 마지막 인덱스도 생략 가능합니다. 그래서 `&a[2..a.len()]` 와 `&a[2..]`는 동일합니다.\n"
|
|
"\n"
|
|
"* 따라서 전체 배열에 대한 슬라이스는 `&a[..]`가 됩니다.\n"
|
|
"\n"
|
|
"* `s`는 `i32`들로 이루어진 슬라이스에 대한 참조입니다. `s`의 타입(`&[i32]`)에 배열의 크기가 빠져있음에 주목하시기 바랍니다. 즉, 슬라이스를 이용하면 다양한 길이의 데이터를 다룰 수 있습니다.\n"
|
|
"\n"
|
|
"* 슬라이스는 항상 다른 객체로부터 '빌려' 옵니다. 이 예시에서 객체 `a`는 슬라이스 `s`보다 더 오래 살아 있어야만 합니다.\n"
|
|
"\n"
|
|
"* `a[3]`의 값을 바꿀 수 있냐는 질문은 좋은 질문입니다. 여기에 대한 답은 `a`와 `s`를 통해 데이터를 읽을 수는 있지만 수정할 수는 없으며, 이는 메모리 안전을 위해서라는 것입니다. 왜 그런지에 대한 좀더 구체적인 답은 빌림 검사 부분에서 자세히 설명합니다."
|
|
|
|
#: src/basic-syntax/string-slices.md:1
|
|
msgid "# `String` vs `str`"
|
|
msgstr "# `String`과 `str`"
|
|
|
|
#: src/basic-syntax/string-slices.md:3
|
|
msgid "We can now understand the two string types in Rust:"
|
|
msgstr "이제 러스트의 두 가지 문자열 타입에 대해서 이해해 보겠습니다:"
|
|
|
|
#: src/basic-syntax/string-slices.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s1: &str = \"World\";\n"
|
|
" println!(\"s1: {s1}\");\n"
|
|
"\n"
|
|
" let mut s2: String = String::from(\"Hello \");\n"
|
|
" println!(\"s2: {s2}\");\n"
|
|
" s2.push_str(s1);\n"
|
|
" println!(\"s2: {s2}\");\n"
|
|
" \n"
|
|
" let s3: &str = &s2[6..];\n"
|
|
" println!(\"s3: {s3}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/string-slices.md:20
|
|
msgid "Rust terminology:"
|
|
msgstr "러스트 용어:"
|
|
|
|
#: src/basic-syntax/string-slices.md:22
|
|
msgid ""
|
|
"* `&str` an immutable reference to a string slice.\n"
|
|
"* `String` a mutable string buffer."
|
|
msgstr ""
|
|
"* `&str`은 문자열 슬라이스에 대한 (불변) 참조입니다.\n"
|
|
"* `String`은 문자열을 담을 수 있는 버퍼입니다."
|
|
|
|
#: src/basic-syntax/string-slices.md:27
|
|
msgid ""
|
|
"* `&str` introduces a string slice, which is an immutable reference to UTF-8 encoded string data \n"
|
|
" stored in a block of memory. String literals (`”Hello”`), are stored in the program’s binary.\n"
|
|
"\n"
|
|
"* Rust’s `String` type is a wrapper around a vector of bytes. As with a `Vec<T>`, it is owned.\n"
|
|
" \n"
|
|
"* As with many other types `String::from()` creates a string from a string literal; `String::new()` \n"
|
|
" creates a new empty string, to which string data can be added using the `push()` and `push_str()` methods.\n"
|
|
"\n"
|
|
"* The `format!()` macro is a convenient way to generate an owned string from dynamic values. It \n"
|
|
" accepts the same format specification as `println!()`.\n"
|
|
" \n"
|
|
"* You can borrow `&str` slices from `String` via `&` and optionally range selection.\n"
|
|
" \n"
|
|
"* For C++ programmers: think of `&str` as `const char*` from C++, but the one that always points \n"
|
|
" to a valid string in memory. Rust `String` is a rough equivalent of `std::string` from C++ \n"
|
|
" (main difference: it can only contain UTF-8 encoded bytes and will never use a small-string optimization).\n"
|
|
" "
|
|
msgstr ""
|
|
"* `&str`은 문자열 슬라이스의 불변 참조입니다. 러스트에서 문자열은 UTF-8로 인코딩된 데이터를 의미합니다. 문자열 리터럴(`\"Hello\"`)은 프로그램 바이너리에 저장됩니다.\n"
|
|
"\n"
|
|
"* 러스트의 `String`타입은 실제로는 문자열을 이루는 바이트에 대한 백터(`Vec<u8>`)입니다. `Vec<T>`가 `T`를 소유하고 있듯이, `String`이 가리키고 있는 문자열은 `String`의 소유입니다.\n"
|
|
"\n"
|
|
"* 다른 많은 타입들처럼 `String::from`는 문자열 리터럴로부터 문자열을 생성합니다. `String::new()`는 새로운 빈 문자열을 생성합니다. `push()`와 `push_str()`메서드를 사용하여 문자열 데이터를 추가 할 수 있습니다.\n"
|
|
"\n"
|
|
"* `format!()` 매크로는 변수의 값을 문자열로 변환하는 편리한 방법입니다. 이 매크로는 `println!()` 매크로와 동일한 포맷팅 형식을 지원합니다.\n"
|
|
"\n"
|
|
"* `&`와 범위 연산자를 이용하여 `String`에서 `&str`슬라이스를 빌려올 수 있습니다.\n"
|
|
"\n"
|
|
"* 당신이 C++ 프로그래머 라면: `&str`는 C++의 `const char*`와 유사하지만 항상 유효한 문자열을 가리킨다는 점이 다릅니다. 러스트의 `String`은 C++의 `std::string` 과 대략 거의 동일합니다. (주요 차이점: 러스트의 `String`은 UTF-8 인코딩 바이트만 포함할 수 있으며 작은 문자열 최적화(small-string optimization)는 사용하지 않습니다."
|
|
|
|
#: src/basic-syntax/functions.md:1
|
|
msgid "# Functions"
|
|
msgstr "# 함수"
|
|
|
|
#: src/basic-syntax/functions.md:3
|
|
msgid "A Rust version of the famous [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) interview question:"
|
|
msgstr "러스트 버전의 [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) 함수입니다:"
|
|
|
|
#: src/basic-syntax/functions.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" fizzbuzz_to(20); // Defined below, no forward declaration needed\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn is_divisible_by(lhs: u32, rhs: u32) -> bool {\n"
|
|
" if rhs == 0 {\n"
|
|
" return false; // Corner case, early return\n"
|
|
" }\n"
|
|
" lhs % rhs == 0 // The last expression in a block is the return value\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn fizzbuzz(n: u32) -> () { // No return value means returning the unit type `()`\n"
|
|
" match (is_divisible_by(n, 3), is_divisible_by(n, 5)) {\n"
|
|
" (true, true) => println!(\"fizzbuzz\"),\n"
|
|
" (true, false) => println!(\"fizz\"),\n"
|
|
" (false, true) => println!(\"buzz\"),\n"
|
|
" (false, false) => println!(\"{n}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn fizzbuzz_to(n: u32) { // `-> ()` is normally omitted\n"
|
|
" for i in 1..=n {\n"
|
|
" fizzbuzz(i);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" fizzbuzz_to(20); // C/C++ 와 달리 호출부 하단에 정의해도 문제 없습니다.\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn is_divisible_by(lhs: u32, rhs: u32) -> bool {\n"
|
|
" if rhs == 0 {\n"
|
|
" return false; // Corner case이므로 반환합니다.\n"
|
|
" }\n"
|
|
" lhs % rhs == 0 // 마지막 표현식은 반환 값입니다.(;없음에 주목)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn fizzbuzz(n: u32) -> () { // `()`는 반환값이 없다는 것을 의미합니다.\n"
|
|
" match (is_divisible_by(n, 3), is_divisible_by(n, 5)) {\n"
|
|
" (true, true) => println!(\"fizzbuzz\"),\n"
|
|
" (true, false) => println!(\"fizz\"),\n"
|
|
" (false, true) => println!(\"buzz\"),\n"
|
|
" (false, false) => println!(\"{n}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn fizzbuzz_to(n: u32) { // `-> ()` 는 일반적으로 생략합니다.\n"
|
|
" for i in 1..=n {\n"
|
|
" fizzbuzz(i);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/basic-syntax/functions.md:35
|
|
msgid ""
|
|
"* We refer in `main` to a function written below. Neither forward declarations nor headers are necessary. \n"
|
|
"* Declaration parameters are followed by a type (the reverse of some programming languages), then a return type.\n"
|
|
"* The last expression in a function body (or any block) becomes the return value. Simply omit the `;` at the end of the expression.\n"
|
|
"* Some functions have no return value, and return the 'unit type', `()`. The compiler will infer this if the `-> ()` return type is omitted.\n"
|
|
"* The range expression in the `for` loop in `fizzbuzz_to()` contains `=n`, which causes it to include the upper bound.\n"
|
|
"* The `match` expression in `fizzbuzz()` is doing a lot of work. It is expanded below to show what is happening.\n"
|
|
"\n"
|
|
" (Type annotations added for clarity, but they can be elided.)\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let by_3: bool = is_divisible_by(n, 3);\n"
|
|
" let by_5: bool = is_divisible_by(n, 5);\n"
|
|
" let by_35: (bool, bool) = (by_3, by_5);\n"
|
|
" match by_35 {\n"
|
|
" // ...\n"
|
|
" ```\n"
|
|
"\n"
|
|
" "
|
|
msgstr ""
|
|
"* `main` 함수에서 그 다음에 오는 함수들을 사용할 수 있습니다. 상단에 선언이나 헤더 같은건 필요 없습니다.\n"
|
|
"* 매개변수를 선언할 때에는 이름을 먼저 쓰고, 타입을 나중에 씁니다. 이름과 타입은 `:` 로 구분합니다. 이는 일부 언어(예를 들어 C)와 반대임에 유의하시기 바랍니다. 마찬가지로, 리턴 타입도 함수의 시작이 아닌 가장 뒷부분에 선언합니다.\n"
|
|
"* 함수 본문의 마지막 표현식은 반환 값이 됩니다. 간단히, 식 끝에 있는 `;`를 생략하면 됩니다.\n"
|
|
"* 반환값이 없는 함수의 경우, 유닛 타입 `()`을 반환합니다. `-> ()`가 생략된 경우 컴파일러는 이를 추론합니다.\n"
|
|
"* `fizzbuzz_to()`함수 내 `for` 반목문의 범위 표현식 중 `=n`은 n까지 포함한다는 의미입니다.\n"
|
|
"* `fizzbuzz()`함수의 `match` 표현식은 많은 일을 합니다. 무슨 일이 일어나는지 조금 더 이해하기 쉽도록 아래 코드를 확인하시기 바랍니다.\n"
|
|
"\n"
|
|
" (명확한 설명을 위해 타입이 명시적으로 선언 되었지만 생략 가능합니다.)\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let by_3: bool = is_divisible_by(n, 3);\n"
|
|
" let by_5: bool = is_divisible_by(n, 5);\n"
|
|
" let by_35: (bool, bool) = (by_3, by_5);\n"
|
|
" match by_35 {\n"
|
|
" // ...\n"
|
|
" ```\n"
|
|
"\n"
|
|
" "
|
|
|
|
#: src/basic-syntax/methods.md:1 src/methods.md:1
|
|
msgid "# Methods"
|
|
msgstr "# 메서드"
|
|
|
|
#: src/basic-syntax/methods.md:3
|
|
msgid ""
|
|
"Rust has methods, they are simply functions that are associated with a particular type. The\n"
|
|
"first argument of a method is an instance of the type it is associated with:"
|
|
msgstr "러스트의 메서드는 특정 타입과 연결된 함수입니다. 메서드의 첫번째 인자의 타입이 바로 그 메서드가 연결된 타입입니다:"
|
|
|
|
#: src/basic-syntax/methods.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Rectangle {\n"
|
|
" width: u32,\n"
|
|
" height: u32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Rectangle {\n"
|
|
" fn area(&self) -> u32 {\n"
|
|
" self.width * self.height\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn inc_width(&mut self, delta: u32) {\n"
|
|
" self.width += delta;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut rect = Rectangle { width: 10, height: 5 };\n"
|
|
" println!(\"old area: {}\", rect.area());\n"
|
|
" rect.inc_width(5);\n"
|
|
" println!(\"new area: {}\", rect.area());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/methods.md:30
|
|
msgid "* We will look much more at methods in today's exercise and in tomorrow's class."
|
|
msgstr "* 오늘과 내일 강의에서 더 많은 메서드 사용법을 다룰 것입니다."
|
|
|
|
#: src/basic-syntax/functions-interlude.md:1
|
|
msgid "# Function Overloading"
|
|
msgstr "# (함수) 오버로딩"
|
|
|
|
#: src/basic-syntax/functions-interlude.md:3
|
|
msgid "Overloading is not supported:"
|
|
msgstr "오버로딩은 지원되지 않습니다:"
|
|
|
|
#: src/basic-syntax/functions-interlude.md:5
|
|
msgid ""
|
|
"* Each function has a single implementation:\n"
|
|
" * Always takes a fixed number of parameters.\n"
|
|
" * Always takes a single set of parameter types.\n"
|
|
"* Default values are not supported:\n"
|
|
" * All call sites have the same number of arguments.\n"
|
|
" * Macros are sometimes used as an alternative."
|
|
msgstr ""
|
|
"* 개별함수는 단일 구현만 갖습니다.\n"
|
|
" * 항상 고정된 수의 파라매터만 갖습니다.\n"
|
|
" * 파라매터들의 타입은 항상 고정되어 있습니다.\n"
|
|
"* 파라매터의 기본 값은 지원되지 않습니다.\n"
|
|
" * 모든 호출부에서는 동일한 수의 인자를 설정해야합니다. \n"
|
|
" * 이런 사항들이 제약이 될 경우, 대안으로 매크로를 사용하기도 합니다."
|
|
|
|
#: src/basic-syntax/functions-interlude.md:12
|
|
msgid "However, function parameters can be generic:"
|
|
msgstr "하지만, 함수의 매개변수는 제네릭을 적용할 수 있습니다:"
|
|
|
|
#: src/basic-syntax/functions-interlude.md:14
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn pick_one<T>(a: T, b: T) -> T {\n"
|
|
" if std::process::id() % 2 == 0 { a } else { b }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"coin toss: {}\", pick_one(\"heads\", \"tails\"));\n"
|
|
" println!(\"cash prize: {}\", pick_one(500, 1000));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/functions-interlude.md:27
|
|
msgid ""
|
|
"* When using generics, the standard library's `Into<T>` can provide a kind of limited\n"
|
|
" polymorphism on argument types. We will see more details in a later section."
|
|
msgstr "* 제네릭을 사용할 때 표준 라이브러리의 `Into<T>`은 타입에 대한 다형성을 제공할 수 있습니다. 나중에 자세히 설명하겠습니다."
|
|
|
|
#: src/basic-syntax/functions-interlude.md:30
|
|
msgid "</defails>"
|
|
msgstr "</defails>"
|
|
|
|
#: src/exercises/day-1/morning.md:1
|
|
msgid "# Day 1: Morning Exercises"
|
|
msgstr "# 1일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-1/morning.md:3
|
|
msgid "In these exercises, we will explore two parts of Rust:"
|
|
msgstr "이번 연습문제는 러스트의 두 부분을 알아볼 것입니다:"
|
|
|
|
#: src/exercises/day-1/morning.md:5
|
|
msgid ""
|
|
"* Implicit conversions between types.\n"
|
|
"\n"
|
|
"* Arrays and `for` loops."
|
|
msgstr ""
|
|
"* 타입의 묵시적 변환.\n"
|
|
"\n"
|
|
"* 배열과 `for` 반복문."
|
|
|
|
#: src/exercises/day-1/morning.md:11
|
|
msgid "A few things to consider while solving the exercises:"
|
|
msgstr "연습문제를 해결하는데 고려해야 할 사항들:"
|
|
|
|
#: src/exercises/day-1/morning.md:13
|
|
msgid ""
|
|
"* Use a local Rust installation, if possible. This way you can get\n"
|
|
" auto-completion in your editor. See the page about [Using Cargo] for details\n"
|
|
" on installing Rust.\n"
|
|
"\n"
|
|
"* Alternatively, use the Rust Playground."
|
|
msgstr ""
|
|
"* 가능하다면 러스트가 설치된 로컬 환경에서 진행하세요. 그러는 편이 텍스트 에디터의 자동완성 기능의 도움을 받을 수 있어서 좋습니다. [카고 사용하기][Using Cargo] 을 참조하시기 바랍니다.\n"
|
|
"\n"
|
|
"* 혹은 러스트 플레이그라운드를 이용할 수 있습니다."
|
|
|
|
#: src/exercises/day-1/morning.md:19
|
|
msgid ""
|
|
"The code snippets are not editable on purpose: the inline code snippets lose\n"
|
|
"their state if you navigate away from the page."
|
|
msgstr "페이지 밖으로 이동할 경우 작성한 내용이 소실되기 때문에 제공되는 코드 스니펫은 의도적으로 편집할 수 없습니다."
|
|
|
|
#: src/exercises/day-1/morning.md:22 src/exercises/day-1/afternoon.md:11
|
|
#: src/exercises/day-2/morning.md:11 src/exercises/day-2/afternoon.md:7
|
|
#: src/exercises/day-3/morning.md:7 src/exercises/day-4/morning.md:12
|
|
msgid "After looking at the exercises, you can look at the [solutions] provided."
|
|
msgstr "연습문제를 살펴 본 후, 제공된 [해답][solutions]을 살펴볼 수 있습니다."
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:1
|
|
msgid "# Implicit Conversions"
|
|
msgstr "# 묵시적 형변환"
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:3
|
|
msgid ""
|
|
"Rust will not automatically apply _implicit conversions_ between types ([unlike\n"
|
|
"C++][3]). You can see this in a program like this:"
|
|
msgstr "러스트는 [C++ 와 다르게][3] 타입 간 *묵시적 변환*을 자동으로 적용하지 않습니다. 아래 예시를 확인해 보세요:"
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:6
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn multiply(x: i16, y: i16) -> i16 {\n"
|
|
" x * y\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let x: i8 = 15;\n"
|
|
" let y: i16 = 1000;\n"
|
|
"\n"
|
|
" println!(\"{x} * {y} = {}\", multiply(x, y));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:19
|
|
msgid ""
|
|
"The Rust integer types all implement the [`From<T>`][1] and [`Into<T>`][2]\n"
|
|
"traits to let us convert between them. The `From<T>` trait has a single `from()`\n"
|
|
"method and similarly, the `Into<T>` trait has a single `into()` method.\n"
|
|
"Implementing these traits is how a type expresses that it can be converted into\n"
|
|
"another type."
|
|
msgstr "러스트의 정수형 타입은 모두 [`From<T>`][1] 와 [`Into<T>`][2] 트레잇을 구현하고 있으며, 이를 통해 타입 변환이 이루어 집니다. `From<T>` 트레잇은 `from()` 메서드를 가지고 있고, `Into<T>`트레잇은 `into()` 메서드를 가지고 있습니다. 러스트에서는 `From`과 `Into` 트레잇을 구현함으로써, 타입 간 변환이 가능하다는 것을 표현합니다."
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:25
|
|
msgid ""
|
|
"The standard library has an implementation of `From<i8> for i16`, which means\n"
|
|
"that we can convert a variable `x` of type `i8` to an `i16` by calling \n"
|
|
"`i16::from(x)`. Or, simpler, with `x.into()`, because `From<i8> for i16`\n"
|
|
"implementation automatically create an implementation of `Into<i16> for i8`."
|
|
msgstr "표준 라이브러리에는 `From<i8> for i16`가 구현되어 있는데 이것은 `i8` 타입의 변수 `x`를 `i16::from(x)`를 호출하여 `i16`타입으로 변환할 수 있다는 의미입니다. 혹은 더 간단하게 `x.into()`를 사용할 수도 있습니다. 이것이 가능한 이유는 `From<i8> for i16` 구현을 가지고 있으면 `Into<i16> for i8` 구현이 자동으로 생성되기 때문입니다."
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:30
|
|
msgid ""
|
|
"The same applies for your own `From` implementations for your own types, so it is\n"
|
|
"sufficient to only implement `From` to get a respective `Into` implementation automatically."
|
|
msgstr "이는 사용자 정의 타입에도 동일하게 적용되는 규칙입니다. 따라서 `From`만을 구현해도 `Into`까지 자동으로 구현이 됩니다."
|
|
|
|
#: src/exercises/day-1/implicit-conversions.md:33
|
|
msgid ""
|
|
"1. Execute the above program and look at the compiler error.\n"
|
|
"\n"
|
|
"2. Update the code above to use `into()` to do the conversion.\n"
|
|
"\n"
|
|
"3. Change the types of `x` and `y` to other things (such as `f32`, `bool`,\n"
|
|
" `i128`) to see which types you can convert to which other types. Try\n"
|
|
" converting small types to big types and the other way around. Check the\n"
|
|
" [standard library documentation][1] to see if `From<T>` is implemented for\n"
|
|
" the pairs you check."
|
|
msgstr ""
|
|
"1. 위 예제코드를 실행하고 어떤 컴파일 에러가 발생하는지 확인해 보세요.\n"
|
|
"\n"
|
|
"2. `into()`를 사용하여 코드를 수정하세요.\n"
|
|
"\n"
|
|
"3. `x`와 `y`를 `f32`이나 `bool`, `i128` 등으로 바꿔서 해당 타입들로 변환이 되는지 확인해보세요. 작은 사이즈 타입에서 큰 사이즈로 변경해보시고 그 반대로도 해보세요. [표준 라이브러리 문서][1]에서 시도해 본 케이스가 구현되어 있는지 확인해 보세요."
|
|
|
|
#: src/exercises/day-1/for-loops.md:1
|
|
msgid "# Arrays and `for` Loops"
|
|
msgstr "# 배열과 `for`반복문"
|
|
|
|
#: src/exercises/day-1/for-loops.md:3
|
|
msgid "We saw that an array can be declared like this:"
|
|
msgstr "배열을 아래와 같이 선언 할 수 있음을 배웠습니다:"
|
|
|
|
#: src/exercises/day-1/for-loops.md:5
|
|
msgid ""
|
|
"```rust\n"
|
|
"let array = [10, 20, 30];\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/for-loops.md:9
|
|
msgid "You can print such an array by asking for its debug representation with `{:?}`:"
|
|
msgstr "배열을 출력하려면 `{:?}`를 씁니다:"
|
|
|
|
#: src/exercises/day-1/for-loops.md:11
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let array = [10, 20, 30];\n"
|
|
" println!(\"array: {array:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/for-loops.md:18
|
|
msgid ""
|
|
"Rust lets you iterate over things like arrays and ranges using the `for`\n"
|
|
"keyword:"
|
|
msgstr "러스트에서는 `for` 키워드를 사용해 배열이나 범위를 반복할 수 있습니다:"
|
|
|
|
#: src/exercises/day-1/for-loops.md:21
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let array = [10, 20, 30];\n"
|
|
" print!(\"Iterating over array:\");\n"
|
|
" for n in array {\n"
|
|
" print!(\" {n}\");\n"
|
|
" }\n"
|
|
" println!();\n"
|
|
"\n"
|
|
" print!(\"Iterating over range:\");\n"
|
|
" for i in 0..3 {\n"
|
|
" print!(\" {}\", array[i]);\n"
|
|
" }\n"
|
|
" println!();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/for-loops.md:38
|
|
msgid ""
|
|
"Use the above to write a function `pretty_print` which pretty-print a matrix and\n"
|
|
"a function `transpose` which will transpose a matrix (turn rows into columns):"
|
|
msgstr "위 코드를 이용해서, 행렬을 예쁘게 출력하는 `pretty_print`함수와, 행렬을 전치(행과 열을 서로 바꾸는)시키는 `transpose`함수를 작성해 보시기 바랍니다:"
|
|
|
|
#: src/exercises/day-1/for-loops.md:41
|
|
msgid ""
|
|
"```bob\n"
|
|
" ⎛⎡1 2 3⎤⎞ ⎡1 4 7⎤\n"
|
|
"\"transpose\"⎜⎢4 5 6⎥⎟ \"==\"⎢2 5 8⎥\n"
|
|
" ⎝⎣7 8 9⎦⎠ ⎣3 6 9⎦\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/for-loops.md:47
|
|
msgid "Hard-code both functions to operate on 3 × 3 matrices."
|
|
msgstr "두 함수 모두 행렬의 크기는 3 x 3 으로 하드코딩 합니다."
|
|
|
|
#: src/exercises/day-1/for-loops.md:49
|
|
msgid ""
|
|
"Copy the code below to <https://play.rust-lang.org/> and implement the\n"
|
|
"functions:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사해서 구현하시면 됩니다:"
|
|
|
|
#: src/exercises/day-1/for-loops.md:52
|
|
msgid ""
|
|
"```rust,should_panic\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn pretty_print(matrix: &[[i32; 3]; 3]) {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let matrix = [\n"
|
|
" [101, 102, 103], // <-- the comment makes rustfmt add a newline\n"
|
|
" [201, 202, 203],\n"
|
|
" [301, 302, 303],\n"
|
|
" ];\n"
|
|
"\n"
|
|
" println!(\"matrix:\");\n"
|
|
" pretty_print(&matrix);\n"
|
|
"\n"
|
|
" let transposed = transpose(matrix);\n"
|
|
" println!(\"transposed:\");\n"
|
|
" pretty_print(&transposed);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,should_panic\n"
|
|
"// TODO: 구현이 완료되면 아래 줄은 삭제합니다.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn pretty_print(matrix: &[[i32; 3]; 3]) {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let matrix = [\n"
|
|
" [101, 102, 103], // <-- the comment makes rustfmt add a newline\n"
|
|
" [201, 202, 203],\n"
|
|
" [301, 302, 303],\n"
|
|
" ];\n"
|
|
"\n"
|
|
" println!(\"matrix:\");\n"
|
|
" pretty_print(&matrix);\n"
|
|
"\n"
|
|
" let transposed = transpose(matrix);\n"
|
|
" println!(\"transposed:\");\n"
|
|
" pretty_print(&transposed);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/exercises/day-1/for-loops.md:80
|
|
msgid "## Bonus Question"
|
|
msgstr "## 보너스 문제"
|
|
|
|
#: src/exercises/day-1/for-loops.md:82
|
|
msgid ""
|
|
"Could you use `&[i32]` slices instead of hard-coded 3 × 3 matrices for your\n"
|
|
"argument and return types? Something like `&[&[i32]]` for a two-dimensional\n"
|
|
"slice-of-slices. Why or why not?"
|
|
msgstr "`&[i32]`슬라이스를 잘 이용하면 행렬 크기를 3 x 3으로 하드코딩 하지 않을 수 있을까요? 예컨데 `&[&[i32]]`는 2차원 슬라이스의 슬라이스 입니다. 가능하다면/하지 않다면 왜 그런가요?"
|
|
|
|
#: src/exercises/day-1/for-loops.md:87
|
|
msgid ""
|
|
"See the [`ndarray` crate](https://docs.rs/ndarray/) for a production quality\n"
|
|
"implementation."
|
|
msgstr "상용 품질의 구현에 대해서는 [`ndarray` 크레이트](https://docs.rs/ndarray/)를 참조하시기 바랍니다."
|
|
|
|
#: src/exercises/day-1/for-loops.md:92
|
|
msgid ""
|
|
"The solution and the answer to the bonus section are available in the \n"
|
|
"[Solution](solutions-morning.md#arrays-and-for-loops) section."
|
|
msgstr "보너스 문제에 대한 답변 역시 [해답](solutions-morning.md#arrays-and-for-loops)에서 확인할 수 있습니다."
|
|
|
|
#: src/basic-syntax/variables.md:1
|
|
msgid "# Variables"
|
|
msgstr "# 변수"
|
|
|
|
#: src/basic-syntax/variables.md:3
|
|
msgid ""
|
|
"Rust provides type safety via static typing. Variable bindings are immutable by\n"
|
|
"default:"
|
|
msgstr "러스트는 정적 타이핑을 통해 타입 안전성을 제공합니다. 변수는 기본적으로 불변(immutable)합니다:"
|
|
|
|
#: src/basic-syntax/variables.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let x: i32 = 10;\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" // x = 20;\n"
|
|
" // println!(\"x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let x: i32 = 10;\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" // x = 20;\n"
|
|
" // println!(\"x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/basic-syntax/variables.md:17
|
|
msgid ""
|
|
"* Due to type inference the `i32` is optional. We will gradually show the types less and less as the course progresses.\n"
|
|
"* Note that since `println!` is a macro, `x` is not moved, even using the function like syntax of `println!(\"x: {}\", x)`"
|
|
msgstr ""
|
|
"* 타입 추론에 덕분에 `i32`는 생략 가능합니다. 강의가 진행될 수록 생략 가능한 부분은 점점 생략할 것입니다.\n"
|
|
"* `println!(\"x: {}\", x)`는 함수 호출 처럼 보이지만 실은 매크로 호출이며 `x`는 이동되지 않습니다."
|
|
|
|
#: src/basic-syntax/type-inference.md:1
|
|
msgid "# Type Inference"
|
|
msgstr "# 타입 추론"
|
|
|
|
#: src/basic-syntax/type-inference.md:3
|
|
msgid "Rust will look at how the variable is _used_ to determine the type:"
|
|
msgstr "러스트는 변수가 어떻게 사용되는지를 보고 그 변수의 타입을 추론합니다:"
|
|
|
|
#: src/basic-syntax/type-inference.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn takes_u32(x: u32) {\n"
|
|
" println!(\"u32: {x}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn takes_i8(y: i8) {\n"
|
|
" println!(\"i8: {y}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let x = 10;\n"
|
|
" let y = 20;\n"
|
|
"\n"
|
|
" takes_u32(x);\n"
|
|
" takes_i8(y);\n"
|
|
" // takes_u32(y);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/type-inference.md:26
|
|
msgid "This slide demonstrates how the Rust compiler infers types based on constraints given by variable declarations and usages."
|
|
msgstr "이 슬라이드는, 러스트 컴파일러가 변수가 어떻게 선언되어 있고, 어떻게 사용되는지를 제약 조건으로 삼아서 변수의 타입을 추론하는 모습을 보여줍니다."
|
|
|
|
#: src/basic-syntax/type-inference.md:28
|
|
msgid ""
|
|
"It is very important to emphasize that variables declared like this are not of some sort of dynamic \"any type\" that can\n"
|
|
"hold any data. The machine code generated by such declaration is identical to the explicit declaration of a type.\n"
|
|
"The compiler does the job for us and helps us write more concise code."
|
|
msgstr "여기서 중요한 것은, 이렇게 명시적인 타입을 생략하고 선언되었다고 해서 \"어떤 타입\"이라도 다 담을 수 있는 타입이 되는 것은 아니라는 점입니다. 명시적인 타입 선언이 있던 없던, 컴파일러가 생성한 머신코드는 동일합니다. 컴파일러는 단지 타입 선언을 생략할 수 있도록 해서 프로그래머가 더 간결한 코드를 쓸 수 있도록 도와줄 뿐입니다."
|
|
|
|
#: src/basic-syntax/type-inference.md:32
|
|
msgid "The following code tells the compiler to copy into a certain generic container without the code ever explicitly specifying the contained type, using `_` as a placeholder:"
|
|
msgstr "아래 코드는, 제네릭 컨테이너를 쓸 때 컨테이터 안에 포함된 데이터의 타입을 명시적으로 쓰지 않고 `_`로 대체하여도 된다는 것을 보여줍니다:"
|
|
|
|
#: src/basic-syntax/type-inference.md:34
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut v = Vec::new();\n"
|
|
" v.push((10, false));\n"
|
|
" v.push((20, true));\n"
|
|
" println!(\"v: {v:?}\");\n"
|
|
"\n"
|
|
" let vv = v.iter().collect::<std::collections::HashSet<_>>();\n"
|
|
" println!(\"vv: {vv:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/type-inference.md:46
|
|
msgid "[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect) relies on `FromIterator`, which [`HashSet`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) implements."
|
|
msgstr "[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.collect)는 [`HashSet`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)을 구현한 `FromIterator`에 의존합니다."
|
|
|
|
#: src/basic-syntax/static-and-const.md:1
|
|
msgid "# Static and Constant Variables"
|
|
msgstr "# 정적변수(static)과 상수(const)"
|
|
|
|
#: src/basic-syntax/static-and-const.md:3
|
|
msgid "Global state is managed with static and constant variables."
|
|
msgstr "프로그램의 글로벌 상태는 결국 정적 변수와 상수로 표현됩니다."
|
|
|
|
#: src/basic-syntax/static-and-const.md:5
|
|
msgid "## `const`"
|
|
msgstr "## 상수(`const`)"
|
|
|
|
#: src/basic-syntax/static-and-const.md:7
|
|
msgid "You can declare compile-time constants:"
|
|
msgstr "컴파일 시 값이 정해지는 상수를 선언할 수 있습니다:"
|
|
|
|
#: src/basic-syntax/static-and-const.md:9
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"const DIGEST_SIZE: usize = 3;\n"
|
|
"const ZERO: Option<u8> = Some(42);\n"
|
|
"\n"
|
|
"fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {\n"
|
|
" let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];\n"
|
|
" for (idx, &b) in text.as_bytes().iter().enumerate() {\n"
|
|
" digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b);\n"
|
|
" }\n"
|
|
" digest\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let digest = compute_digest(\"Hello\");\n"
|
|
" println!(\"Digest: {digest:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/static-and-const.md:27
|
|
msgid "According the the [Rust RFC Book][1] these are inlined upon use."
|
|
msgstr "[Rust RFC Book][1]에 따르면 상수는, 그 상수가 사용되는 곳에 인라인 됩니다."
|
|
|
|
#: src/basic-syntax/static-and-const.md:29
|
|
msgid "## `static`"
|
|
msgstr "## 정적변수(`static`)"
|
|
|
|
#: src/basic-syntax/static-and-const.md:31
|
|
msgid "You can also declare static variables:"
|
|
msgstr "마찬가지로 정적 변수도 선언할 수 있습니다:"
|
|
|
|
#: src/basic-syntax/static-and-const.md:33
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"static BANNER: &str = \"Welcome to RustOS 3.14\";\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"{BANNER}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"static BANNER: &str = \"Welcome to RustOS 3.14\";\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"{BANNER}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/basic-syntax/static-and-const.md:41
|
|
msgid "As noted in the [Rust RFC Book][1], these are not inlined upon use and have an actual associated memory location. This is useful for unsafe and embedded code, and the variable lives through the entirety of the program execution."
|
|
msgstr "[Rust RFC Book][1]에서 언급한 바와 같이, 정적 변수는 별도의 메모리 공간을 가지며, 인라인 되지 않습니다. 정적 변수는 안전하지 않은(unsafe) 러스트와 임베디드 시스템용 코드에서 유용합니다. 이들의 수명은 프로그램이 수행되는 전체 시간과 동일합니다."
|
|
|
|
#: src/basic-syntax/static-and-const.md:44
|
|
msgid "We will look at mutating static data in the [chapter on Unsafe Rust](../unsafe.md)."
|
|
msgstr "가변 정적 데이터에 대해서는 [안전하지 않은 러스트](../unsafe.md)에서 살펴봅니다."
|
|
|
|
#: src/basic-syntax/static-and-const.md:48
|
|
msgid ""
|
|
"* Mention that `const` behaves semantically similar to C++'s `constexpr`.\n"
|
|
"* `static`, on the other hand, is much more similar to a `const` or mutable global variable in C++.\n"
|
|
"* It isn't super common that one would need a runtime evaluated constant, but it is helpful and safer than using a static."
|
|
msgstr ""
|
|
"* `const`는 C++의 `constexpr`과 매우 비슷합니다.\n"
|
|
"* 반면에 `static`은 C++의 `const`나 가변 정적 변수와 훨씬 더 유사합니다.\n"
|
|
"* 프로그램 수행시 그 값이 정해지는 상수가 필요한 경우는 드뭅니다. 그러나 그렇다고 해도, 정적 변수를 사용하는 것 보다는 더 유용하고 안전합니다."
|
|
|
|
#: src/basic-syntax/scopes-shadowing.md:1
|
|
msgid "# Scopes and Shadowing"
|
|
msgstr "# 범위(Scope)와 쉐도잉(Shadowing)"
|
|
|
|
#: src/basic-syntax/scopes-shadowing.md:3
|
|
msgid ""
|
|
"You can shadow variables, both those from outer scopes and variables from the\n"
|
|
"same scope:"
|
|
msgstr "현재 범위에 있는 변수와, 바깥 범위에 있는 변수 모두 가릴(쉐도잉)수 있습니다:"
|
|
|
|
#: src/basic-syntax/scopes-shadowing.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let a = 10;\n"
|
|
" println!(\"before: {a}\");\n"
|
|
"\n"
|
|
" {\n"
|
|
" let a = \"hello\";\n"
|
|
" println!(\"inner scope: {a}\");\n"
|
|
"\n"
|
|
" let a = true;\n"
|
|
" println!(\"shadowed in inner scope: {a}\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"after: {a}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/basic-syntax/scopes-shadowing.md:25
|
|
msgid ""
|
|
"* Definition: Shadowing is different from mutation, because after shadowing both variable's memory locations exist at the same time. Both are available under the same name, depending where you use it in the code. \n"
|
|
"* A shadowing variable can have a different type. \n"
|
|
"* Shadowing looks obscure at first, but is convenient for holding on to values after `.unwrap()`.\n"
|
|
"* The following code demonstrates why the compiler can't simply reuse memory locations when shadowing an immutable variable in a scope, even if the type does not change."
|
|
msgstr ""
|
|
"* 쉐도잉은 기존 변수에 새로운 값을 할당하는 것이 아닙니다. 쉐도잉을 하면 새로운 변수가 생기며, 이전 변수와 새 변수는 메모리의 서로 다른 위치에 존재합니다. 그 두 변수는 단지 이름이 같은 뿐이며, 코드 중 어디에서 그 이름이 사용되었느냐에 따라 어떤 변수를 지칭하는 지가 결정됩니다.\n"
|
|
"* 쉐도잉 시 타입을 바꿀 수 있습니다.\n"
|
|
"* 처음에 쉐도잉을 보면 코드를 더 모호하게 만든다고 생각할 수 도 있습니다. 그러나 실제로 쉐도잉을 이용하면, 어떤 변수에서 `.unwrap()` 된 값을 새로운 변수에 담을 경우 새로운 이름을 지을 필요 없이 기존 이름을 유지할 수 있어서 편리합니다.\n"
|
|
"* 아래 코드는 불변 변수를 쉐도잉할 때 타입이 동일하더라도 새 변수가 원래 변수의 메모리 위치를 재사용 할 수 없는지 그 이유를 보여줍니다."
|
|
|
|
#: src/basic-syntax/scopes-shadowing.md:30
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let a = 1;\n"
|
|
" let b = &a;\n"
|
|
" let a = a + 1;\n"
|
|
" println!(\"{a} {b}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management.md:1
|
|
msgid "# Memory Management"
|
|
msgstr "# 메모리 관리"
|
|
|
|
#: src/memory-management.md:3
|
|
msgid "Traditionally, languages have fallen into two broad categories:"
|
|
msgstr "전통적으로, 두 종류의 프로그래밍 언어가 있습니다:"
|
|
|
|
#: src/memory-management.md:5
|
|
msgid ""
|
|
"* Full control via manual memory management: C, C++, Pascal, ...\n"
|
|
"* Full safety via automatic memory management at runtime: Java, Python, Go, Haskell, ..."
|
|
msgstr ""
|
|
"* 메모리 관리가 프로그래머의 완전한 통제하에 있지만 수동(그래서 안전하지 않을 수 있는)인 언어: C, C++, Pascal, ...\n"
|
|
"* 메모리 관리가 런타임에 의해 되므로 안전하지만 자동(그래서 프로그래머가 개입할 여지가 적거나 없는)인 언어: Java, Python, Go, Haskell, ..."
|
|
|
|
#: src/memory-management.md:8
|
|
msgid "Rust offers a new mix:"
|
|
msgstr "러스트는 이 둘을 혼합한 새로운 형태의 메모리 관리 기법을 제공합니다:"
|
|
|
|
#: src/memory-management.md:10
|
|
msgid ""
|
|
"> Full control *and* safety via compile time enforcement of correct memory\n"
|
|
"> management."
|
|
msgstr "> 컴파일 시 올바른 메모리 관리를 강제함으로써 완전한 통제와 안전성 모두 제공"
|
|
|
|
#: src/memory-management.md:13
|
|
msgid "It does this with an explicit ownership concept."
|
|
msgstr "이를 가능하게 하는 러스트의 컨셉은 명시적인 소유권입니다."
|
|
|
|
#: src/memory-management.md:15
|
|
msgid "First, let's refresh how memory management works."
|
|
msgstr "우선 메모리 관리가 이뤄지는 방식을 다시 살펴 보겠습니다."
|
|
|
|
#: src/memory-management/stack-vs-heap.md:1
|
|
msgid "# The Stack vs The Heap"
|
|
msgstr "# 스택(Stack)과 힙(Heap)"
|
|
|
|
#: src/memory-management/stack-vs-heap.md:3
|
|
msgid ""
|
|
"* Stack: Continuous area of memory for local variables.\n"
|
|
" * Values have fixed sizes known at compile time.\n"
|
|
" * Extremely fast: just move a stack pointer.\n"
|
|
" * Easy to manage: follows function calls.\n"
|
|
" * Great memory locality.\n"
|
|
"\n"
|
|
"* Heap: Storage of values outside of function calls.\n"
|
|
" * Values have dynamic sizes determined at runtime.\n"
|
|
" * Slightly slower than the stack: some book-keeping needed.\n"
|
|
" * No guarantee of memory locality."
|
|
msgstr ""
|
|
"* 스택: 지역 변수를 위한 연속적인 메모리 영역\n"
|
|
" * 여기 저장되는 값은 컴파일 시 결정되는 고정 크기를 갖습니다. \n"
|
|
" * 매우 빠름: 메모리 할당/반환이 단지 스택 포인터의 이동만으로 구현됩니다.\n"
|
|
" * 관리가 쉬움: 함수가 호출되면 할당되고, 리턴하면 반환됩니다.\n"
|
|
" * 스택에 있는 값들은 매우 높은 메모리 인접성을 가집니다(_역주_: 그래서 캐시를 효과적으로 활용할 수 있습니다)\n"
|
|
"\n"
|
|
"* 힙: 함수 호출/리턴과 상관 없이 유지되는 값이 저장되는 곳\n"
|
|
" * 여기 저장되는 값은 프로그램 수행시 그 크기가 결정됩니다.\n"
|
|
" * 스택 보다는 느림: 메모리 할당/반환시 해야 할 일이 좀 더 있습니다.\n"
|
|
" * 메모리 인접성을 보장하지 않습니다."
|
|
|
|
#: src/memory-management/stack.md:1
|
|
msgid "# Stack Memory"
|
|
msgstr "# 스택 메모리"
|
|
|
|
#: src/memory-management/stack.md:3
|
|
msgid ""
|
|
"Creating a `String` puts fixed-sized data on the stack and dynamically sized\n"
|
|
"data on the heap:"
|
|
msgstr "`String` 타입은 고정 크기 데이터(_역주_: 문자열의 길이, 문자열이 저장된 버퍼의 주소 등)는 스택에 저장하고, 가변 크기 데이터(_역주_: 문자열 버퍼)는 힙에 저장합니다:"
|
|
|
|
#: src/memory-management/stack.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s1 = String::from(\"Hello\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/stack.md:12
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": s1 : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+----+----+ :\n"
|
|
": | ptr | o---+---+-----+-->| H | e | l | l | o | :\n"
|
|
": | len | 5 | : : +----+----+----+----+----+ :\n"
|
|
": | capacity | 5 | : : :\n"
|
|
": +-----------+-------+ : : :\n"
|
|
": : `- - - - - - - - - - - - - - - -'\n"
|
|
"`- - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/stack.md:28
|
|
msgid ""
|
|
"* Mention that a `String` is backed by a `Vec`, so it has a capacity and length and can grow if mutable via reallocation on the heap.\n"
|
|
"\n"
|
|
"* If students ask about it, you can mention that the underlying memory is heap allocated using the [System Allocator] and custom allocators can be implemented using the [Allocator API]\n"
|
|
"\n"
|
|
"* We can inspect the memory layout with `unsafe` code. However, you should point out that this is rightfully unsafe!\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" fn main() {\n"
|
|
" let mut s1 = String::from(\"Hello\");\n"
|
|
" s1.push(' ');\n"
|
|
" s1.push_str(\"world\");\n"
|
|
" // DON'T DO THIS AT HOME! For educational purposes only.\n"
|
|
" // String provides no guarantees about its layout, so this could lead to\n"
|
|
" // undefined behavior.\n"
|
|
" unsafe {\n"
|
|
" let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);\n"
|
|
" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```"
|
|
msgstr ""
|
|
"* 문자열(`String`)은 실제로는 `Vec`입니다. 크기(capacity)와 현재 길이(length) 정보를 가지며, 더 큰 크기가 필요할 경우 힙에서 재 할당을 합니다.\n"
|
|
"\n"
|
|
"* 힙은 기본적으로 [System Allocator]를 통해 할당됩니다. 그리고 [Allocator API]를 이용해서 커스텀 메모리 할당자를 만들 수도 있습니다.\n"
|
|
"\n"
|
|
"* `unsafe` 코드로 메모리 레이아웃을 살펴볼 수 있습니다. 물론 이 코드가 안전하지 않다는 점을 알려주세요!\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" fn main() {\n"
|
|
" let mut s1 = String::from(\"Hello\");\n"
|
|
" s1.push(' ');\n"
|
|
" s1.push_str(\"world\");\n"
|
|
" // DON'T DO THIS AT HOME! For educational purposes only.\n"
|
|
" // String provides no guarantees about its layout, so this could lead to\n"
|
|
" // undefined behavior.\n"
|
|
" unsafe {\n"
|
|
" let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);\n"
|
|
" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```"
|
|
|
|
#: src/memory-management/manual.md:1
|
|
msgid "# Manual Memory Management"
|
|
msgstr "# 수동 메모리 관리"
|
|
|
|
#: src/memory-management/manual.md:3
|
|
msgid "You allocate and deallocate heap memory yourself."
|
|
msgstr "사용자가 직접 메모리를 할당, 해제 합니다."
|
|
|
|
#: src/memory-management/manual.md:5
|
|
msgid "If not done with care, this can lead to crashes, bugs, security vulnerabilities, and memory leaks."
|
|
msgstr "조심하지 않으면, 충돌(crash), 버그, 보안취약성 및 메모리 누출이 발생할 수 있습니다."
|
|
|
|
#: src/memory-management/manual.md:7
|
|
msgid "## C Example"
|
|
msgstr "## C 언어 예제"
|
|
|
|
#: src/memory-management/manual.md:9
|
|
msgid "You must call `free` on every pointer you allocate with `malloc`:"
|
|
msgstr "`malloc`으로 할당하는 포인터마다 `free`를 호출해야 합니다:"
|
|
|
|
#: src/memory-management/manual.md:11
|
|
msgid ""
|
|
"```c\n"
|
|
"void foo(size_t n) {\n"
|
|
" int* int_array = (int*)malloc(n * sizeof(int));\n"
|
|
" //\n"
|
|
" // ... lots of code\n"
|
|
" //\n"
|
|
" free(int_array);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/manual.md:21
|
|
msgid ""
|
|
"Memory is leaked if the function returns early between `malloc` and `free`: the\n"
|
|
"pointer is lost and we cannot deallocate the memory."
|
|
msgstr "만약 `malloc` 과 `free` 사이에서 함수가 일찍 반환되면 메모리 누출이 일어납니다: 포인터를 잃어버리게 되어 메모리를 반환할 수 없게 됩니다."
|
|
|
|
#: src/memory-management/scope-based.md:1
|
|
msgid "# Scope-Based Memory Management"
|
|
msgstr "# 범위기반 메모리 관리"
|
|
|
|
#: src/memory-management/scope-based.md:3
|
|
msgid "Constructors and destructors let you hook into the lifetime of an object."
|
|
msgstr "생성자와 소멸자를 사용하여 객체의 생명주기에 따라 메모리 할당/해제가 일어나도록 할 수 있습니다."
|
|
|
|
#: src/memory-management/scope-based.md:5
|
|
msgid ""
|
|
"By wrapping a pointer in an object, you can free memory when the object is\n"
|
|
"destroyed. The compiler guarantees that this happens, even if an exception is\n"
|
|
"raised."
|
|
msgstr "포인터를 객체로 감싸도록 하면, 그 객체가 소멸될 때 그 포인터가 가리키는 메모리가 해제되도록 할 수 있습니다. 컴파일러는 객체가 소멸될 때 반드시 소멸자가 호출되는 것을 보장합니다. 심지어는 예외(exception)가 발생(_역주_: 함수의 리턴이나 스코프의 종료 뿐만이 아니라) 하더라도요."
|
|
|
|
#: src/memory-management/scope-based.md:9
|
|
msgid ""
|
|
"This is often called _resource acquisition is initialization_ (RAII) and gives\n"
|
|
"you smart pointers."
|
|
msgstr "이를 종종 RAII (Resource Acquisition Is Initialization)라고 하며, 이런 객체는 일종의 스마트 포인터 역할을 합니다."
|
|
|
|
#: src/memory-management/scope-based.md:12
|
|
msgid "## C++ Example"
|
|
msgstr "## C++ 예제"
|
|
|
|
#: src/memory-management/scope-based.md:14
|
|
msgid ""
|
|
"```c++\n"
|
|
"void say_hello(std::unique_ptr<Person> person) {\n"
|
|
" std::cout << \"Hello \" << person->name << std::endl;\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/scope-based.md:20
|
|
msgid ""
|
|
"* The `std::unique_ptr` object is allocated on the stack, and points to\n"
|
|
" memory allocated on the heap.\n"
|
|
"* At the end of `say_hello`, the `std::unique_ptr` destructor will run.\n"
|
|
"* The destructor frees the `Person` object it points to."
|
|
msgstr ""
|
|
"* `std::unique_ptr`객체는 스택에 할당되며, 힙에 할당된 메모리를 가리킵니다.\n"
|
|
"* `say_hello`함수가 끝나면 `std::unique_ptr`의 소멸자가 실행됩니다.\n"
|
|
"* 소멸자는 `Person` 객체가 가리키는 메모리를 해제합니다."
|
|
|
|
#: src/memory-management/scope-based.md:25
|
|
msgid "Special move constructors are used when passing ownership to a function:"
|
|
msgstr "이동 생성자는 함수 호출 시 소유권을 전달할때 사용됩니다:"
|
|
|
|
#: src/memory-management/scope-based.md:27
|
|
msgid ""
|
|
"```c++\n"
|
|
"std::unique_ptr<Person> person = find_person(\"Carla\");\n"
|
|
"say_hello(std::move(person));\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/garbage-collection.md:1
|
|
msgid "# Automatic Memory Management"
|
|
msgstr "# 자동 메모리 관리"
|
|
|
|
#: src/memory-management/garbage-collection.md:3
|
|
msgid ""
|
|
"An alternative to manual and scope-based memory management is automatic memory\n"
|
|
"management:"
|
|
msgstr "수동, 스코프기반 메모리 관리의 대안으로 자동 메모리 관리 방식이 있습니다:"
|
|
|
|
#: src/memory-management/garbage-collection.md:6
|
|
msgid ""
|
|
"* The programmer never allocates or deallocates memory explicitly.\n"
|
|
"* A garbage collector finds unused memory and deallocates it for the programmer."
|
|
msgstr ""
|
|
"* 개발자는 메모리를 명시적으로 할당/해제 하지 않습니다.\n"
|
|
"* 가비지 컬렉터(GC)는 사용되지 않는 메모리를 찾아 해제합니다."
|
|
|
|
#: src/memory-management/garbage-collection.md:9
|
|
msgid "## Java Example"
|
|
msgstr "## Jave 예제"
|
|
|
|
#: src/memory-management/garbage-collection.md:11
|
|
msgid "The `person` object is not deallocated after `sayHello` returns:"
|
|
msgstr "`person`객체는 `sayHello`함수 반환 후에도 해제되지 않습니다. (_역주_: GC가 나중에 알아서 해제합니다.)"
|
|
|
|
#: src/memory-management/garbage-collection.md:13
|
|
msgid ""
|
|
"```java\n"
|
|
"void sayHello(Person person) {\n"
|
|
" System.out.println(\"Hello \" + person.getName());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/memory-management/rust.md:1
|
|
msgid "# Memory Management in Rust"
|
|
msgstr "# 러스트에서의 메모리 관리"
|
|
|
|
#: src/memory-management/rust.md:3
|
|
msgid "Memory management in Rust is a mix:"
|
|
msgstr "러스트의 메모리 관리는 지금까지 설명한 방식들을 혼합해서 사용합니다:"
|
|
|
|
#: src/memory-management/rust.md:5
|
|
msgid ""
|
|
"* Safe and correct like Java, but without a garbage collector.\n"
|
|
"* Depending on which abstraction (or combination of abstractions) you choose, can be a single unique pointer, reference counted, or atomically reference counted.\n"
|
|
"* Scope-based like C++, but the compiler enforces full adherence.\n"
|
|
"* A Rust user can choose the right abstraction for the situation, some even have no cost at runtime like C."
|
|
msgstr ""
|
|
"* 자바처럼 안전하고 정확합니다. 하지만 GC는 없습니다.\n"
|
|
"* 다양한 추상화를 제공합니다: 단일 포인터, 참조 카운트, 아토믹(atomic) 참조 카운트.\n"
|
|
"* C++ 처럼 범위(스코프) 기반입니다. 하지만 컴파일러가 훨씬 더 엄격합니다.\n"
|
|
"* 사용자는 상황에 따라 적합한 추상화를 선택할 수 있습니다. 그 중에는 C 언어 처럼 런타임 오버헤드가 없는 것도 있습니다."
|
|
|
|
#: src/memory-management/rust.md:10
|
|
msgid "It achieves this by modeling _ownership_ explicitly."
|
|
msgstr "러스트는 *소유권*을 언어 차원에서 명시적으로 모델링 함으로써 이를 이룹니다."
|
|
|
|
#: src/memory-management/rust.md:14
|
|
msgid ""
|
|
"* If asked how at this point, you can mention that in Rust this is usually handled by RAII wrapper types such as [Box], [Vec], [Rc], or [Arc]. These encapsulate ownership and memory allocation via various means, and prevent the potential errors in C.\n"
|
|
"\n"
|
|
"* You may be asked about destructors here, the [Drop] trait is the Rust equivalent."
|
|
msgstr ""
|
|
"* 이 시점에서 그게 어떻게 가능하냐는 질문이 있으면, 러스트에서 이 작업은 일반적으로 [Box], [Vec], [Rc] 또는 [Arc]와 같은 RAII 타입에 의해 처리된다고 답변할 수 있습니다. 이들은 다양한 방법을 통해 소유권과 메모리 할당에 대한 구체적인 내용을을 캡슐화하여, C 언어였다면 발생할 수 있었을 다양한 에러를 막습니다.\n"
|
|
"\n"
|
|
"* 소멸자에 대한 질문도 있을 수 있습니다. [Drop] 트레잇이 답입니다."
|
|
|
|
#: src/memory-management/comparison.md:1
|
|
msgid "# Comparison"
|
|
msgstr "# 비교"
|
|
|
|
#: src/memory-management/comparison.md:3
|
|
msgid "Here is a rough comparison of the memory management techniques."
|
|
msgstr "메모리 관리 기술의 대략적인 비교입니다."
|
|
|
|
#: src/memory-management/comparison.md:5
|
|
msgid "## Pros of Different Memory Management Techniques"
|
|
msgstr "## 메모리 관리 방법 별 장점"
|
|
|
|
#: src/memory-management/comparison.md:7
|
|
msgid ""
|
|
"* Manual like C:\n"
|
|
" * No runtime overhead.\n"
|
|
"* Automatic like Java:\n"
|
|
" * Fully automatic.\n"
|
|
" * Safe and correct.\n"
|
|
"* Scope-based like C++:\n"
|
|
" * Partially automatic.\n"
|
|
" * No runtime overhead.\n"
|
|
"* Compiler-enforced scope-based like Rust:\n"
|
|
" * Enforced by compiler.\n"
|
|
" * No runtime overhead.\n"
|
|
" * Safe and correct."
|
|
msgstr ""
|
|
"* C와 같은 수동 관리: \n"
|
|
" * 런타임 오버헤드가 없음. \n"
|
|
"* JAVA와 같은 자동화 관리: \n"
|
|
" * 완전한 자동화.\n"
|
|
" * 안전하고 정확함.\n"
|
|
"* C++ 와 같은 범위 기반 관리: \n"
|
|
" * 부분 자동화\n"
|
|
" * 런타임 오버헤드가 없음.\n"
|
|
"* 러스트와 같은 컴파일러 수행 범위 기반 관리: \n"
|
|
" * 컴파일러에 의해 수행됩니다.\n"
|
|
" * 런타임 오버헤드가 없습니다. \n"
|
|
" * 안전하고 정확합니다."
|
|
|
|
#: src/memory-management/comparison.md:20
|
|
msgid "## Cons of Different Memory Management Techniques"
|
|
msgstr "## 메모리 관리 방법 별 단점"
|
|
|
|
#: src/memory-management/comparison.md:22
|
|
msgid ""
|
|
"* Manual like C:\n"
|
|
" * Use-after-free.\n"
|
|
" * Double-frees.\n"
|
|
" * Memory leaks.\n"
|
|
"* Automatic like Java:\n"
|
|
" * Garbage collection pauses.\n"
|
|
" * Destructor delays.\n"
|
|
"* Scope-based like C++:\n"
|
|
" * Complex, opt-in by programmer.\n"
|
|
" * Potential for use-after-free.\n"
|
|
"* Compiler-enforced and scope-based like Rust:\n"
|
|
" * Some upfront complexity.\n"
|
|
" * Can reject valid programs."
|
|
msgstr ""
|
|
"* C와 같은 수동 관리:\n"
|
|
" * 사용 후 해제 문제.\n"
|
|
" * 이중 해제 문제.\n"
|
|
" * 메모리 누출 문제.\n"
|
|
"* JAVA와 같은 자동화 관리:\n"
|
|
" * GC동작으로 인한 멈춤.\n"
|
|
" * 소멸자 지연 (_역주_: 특정 메모리를 더이상 사용하지 않더라도 곧바로 해제 되지 않고 GC가 동작할 때 까지 기다려야 한다는 점)\n"
|
|
"* C++ 와 같은 범위 기반 관리:\n"
|
|
" * 복잡하며, 개발자의 선택사항임.\n"
|
|
" * 사용 후 해제 문제 가능성 있음.\n"
|
|
"* 러스트와 같은 컴파일러가 강제하는 수행 범위 기반 관리:\n"
|
|
" * 약간의 초기 복잡성.\n"
|
|
" * 올바른 프로그램이지만 컴파일러가 거부할 수 있음."
|
|
|
|
#: src/ownership.md:1
|
|
msgid "# Ownership"
|
|
msgstr "# 소유권"
|
|
|
|
#: src/ownership.md:3
|
|
msgid ""
|
|
"All variable bindings have a _scope_ where they are valid and it is an error to\n"
|
|
"use a variable outside its scope:"
|
|
msgstr "모든 변수 바인딩은 유효한 \"범위(스코프)\"를 가지며, 범위 밖에서 변수 사용하면 에러가 발생합니다:"
|
|
|
|
#: src/ownership.md:6
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" {\n"
|
|
" let p = Point(3, 4);\n"
|
|
" println!(\"x: {}\", p.0);\n"
|
|
" }\n"
|
|
" println!(\"y: {}\", p.1);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable,compile_fail\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" {\n"
|
|
" let p = Point(3, 4);\n"
|
|
" println!(\"x: {}\", p.0);\n"
|
|
" }\n"
|
|
" println!(\"y: {}\", p.1);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/ownership.md:18
|
|
msgid ""
|
|
"* At the end of the scope, the variable is _dropped_ and the data is freed.\n"
|
|
"* A destructor can run here to free up resources.\n"
|
|
"* We say that the variable _owns_ the value."
|
|
msgstr ""
|
|
"* 스코프가 종료되면 변수는 \"삭제(drop)\"되었다고 하며 그 변수의 데이터는 메모리에서 해제됩니다.\n"
|
|
"* 스코프가 종료될 때 다른 리소스를 해제하기 위해 소멸자가 호출되도록 할 수 있습니다.\n"
|
|
"* 이것을 두고 변수가 값을 \"소유\"한다고 표현합니다."
|
|
|
|
#: src/ownership/move-semantics.md:1
|
|
msgid "# Move Semantics"
|
|
msgstr "# Move 문법(Move Semantics)"
|
|
|
|
#: src/ownership/move-semantics.md:3
|
|
msgid "An assignment will transfer ownership between variables:"
|
|
msgstr "(변수의) 할당은 소유권을 변수 간에 이동시킵니다:"
|
|
|
|
#: src/ownership/move-semantics.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s1: String = String::from(\"Hello!\");\n"
|
|
" let s2: String = s1;\n"
|
|
" println!(\"s2: {s2}\");\n"
|
|
" // println!(\"s1: {s1}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/move-semantics.md:14
|
|
msgid ""
|
|
"* The assignment of `s1` to `s2` transfers ownership.\n"
|
|
"* The data was _moved_ from `s1` and `s1` is no longer accessible.\n"
|
|
"* When `s1` goes out of scope, nothing happens: it has no ownership.\n"
|
|
"* When `s2` goes out of scope, the string data is freed.\n"
|
|
"* There is always _exactly_ one variable binding which owns a value."
|
|
msgstr ""
|
|
"* `s1`을 `s2`에 할당하여 소유권을 이전시킵니다.\n"
|
|
"* 데이터는 `s1`에서 _이동_됩니다. 따라서 프로그래머는 `s1`은 더 이상 접근 할 수 없습니다.\n"
|
|
"* `s1`의 스코프가 종료되면 아무 일도 없습니다: 왜냐하면 `s1`은 아무런 소유권이 없기 때문입니다.\n"
|
|
"* `s2`의 스코프가 종료되면 문자열 데이터는 해제됩니다.\n"
|
|
"* 값(데이터)의 소유권을 갖는 변수는 항상 *단* 하나 입니다."
|
|
|
|
#: src/ownership/move-semantics.md:22
|
|
msgid ""
|
|
"* Mention that this is the opposite of the defaults in C++, which copies by value unless you use `std::move` (and the move constructor is defined!).\n"
|
|
"\n"
|
|
"* In Rust, clones are explicit (by using `clone`)."
|
|
msgstr ""
|
|
"* 이는 C++과 정반대 임을 설명하세요. C++에서는 복사가 기본이고, `std::move` 를 이용해야만 (그리고 이동 생성자가 정의되어 있어야만!) 소유권 이전이 됩니다.\n"
|
|
"\n"
|
|
"* 러스트에서는 복사할때에는 명시적으로 `clone`을 사용합니다."
|
|
|
|
#: src/ownership/moved-strings-rust.md:1
|
|
msgid "# Moved Strings in Rust"
|
|
msgstr "# 러스트에서의 문자열 이동(Move)"
|
|
|
|
#: src/ownership/moved-strings-rust.md:3
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s1: String = String::from(\"Rust\");\n"
|
|
" let s2: String = s1;\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/moved-strings-rust.md:10
|
|
msgid ""
|
|
"* The heap data from `s1` is reused for `s2`.\n"
|
|
"* When `s1` goes out of scope, nothing happens (it has been moved from)."
|
|
msgstr ""
|
|
"* `s1`의 힙 데이터는 `s2`에서 재사용 됩니다.\n"
|
|
"* `s1`의 스코프가 종료되면 아무일도 일어나지 않습니다.(이미 이동되었습니다.)"
|
|
|
|
#: src/ownership/moved-strings-rust.md:13
|
|
msgid "Before move to `s2`:"
|
|
msgstr "`s2`로 이동 전 메모리:"
|
|
|
|
#: src/ownership/moved-strings-rust.md:15
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": s1 : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+----+ :\n"
|
|
": | ptr | o---+---+-----+-->| R | u | s | t | :\n"
|
|
": | len | 4 | : : +----+----+----+----+ :\n"
|
|
": | capacity | 4 | : : :\n"
|
|
": +-----------+-------+ : : :\n"
|
|
": : `- - - - - - - - - - - - - -'\n"
|
|
": :\n"
|
|
"`- - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/moved-strings-rust.md:30
|
|
msgid "After move to `s2`:"
|
|
msgstr "`s2`로 이동 후 메모리:"
|
|
|
|
#: src/ownership/moved-strings-rust.md:32
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": s1 \"(inaccessible)\" : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+----+ :\n"
|
|
": | ptr | o---+---+--+--+-->| R | u | s | t | :\n"
|
|
": | len | 4 | : | : +----+----+----+----+ :\n"
|
|
": | capacity | 4 | : | : :\n"
|
|
": +-----------+-------+ : | : :\n"
|
|
": : | `- - - - - - - - - - - - - -'\n"
|
|
": s2 : |\n"
|
|
": +-----------+-------+ : |\n"
|
|
": | ptr | o---+---+--'\n"
|
|
": | len | 4 | :\n"
|
|
": | capacity | 4 | :\n"
|
|
": +-----------+-------+ :\n"
|
|
": :\n"
|
|
"`- - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:1
|
|
msgid "# Double Frees in Modern C++"
|
|
msgstr "# Modern C++에서 이중해제 문제"
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:3
|
|
msgid "Modern C++ solves this differently:"
|
|
msgstr "Modern C++은 이 문제를 다르게 해결합니다:"
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:5
|
|
msgid ""
|
|
"```c++\n"
|
|
"std::string s1 = \"Cpp\";\n"
|
|
"std::string s2 = s1; // Duplicate the data in s1.\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:10
|
|
msgid ""
|
|
"* The heap data from `s1` is duplicated and `s2` gets its own independent copy.\n"
|
|
"* When `s1` and `s2` go out of scope, they each free their own memory."
|
|
msgstr ""
|
|
"* `s1`의 힙 데이터는 복제되고, `s2`는 독립적인 복사본을 얻습니다.\n"
|
|
"* `s1` 와 `s2`의 스코프가 종료되면 각각의 메모리가 해제됩니다."
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:13
|
|
msgid "Before copy-assignment:"
|
|
msgstr "복사 전:"
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:16
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": s1 : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+ :\n"
|
|
": | ptr | o---+---+--+--+-->| C | p | p | :\n"
|
|
": | len | 3 | : : +----+----+----+ :\n"
|
|
": | capacity | 3 | : : :\n"
|
|
": +-----------+-------+ : : :\n"
|
|
": : `- - - - - - - - - - - -'\n"
|
|
"`- - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:30
|
|
msgid "After copy-assignment:"
|
|
msgstr "복사 후:"
|
|
|
|
#: src/ownership/double-free-modern-cpp.md:32
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": s1 : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+ :\n"
|
|
": | ptr | o---+---+--+--+-->| C | p | p | :\n"
|
|
": | len | 3 | : : +----+----+----+ :\n"
|
|
": | capacity | 3 | : : :\n"
|
|
": +-----------+-------+ : : :\n"
|
|
": : : :\n"
|
|
": s2 : : :\n"
|
|
": +-----------+-------+ : : +----+----+----+ :\n"
|
|
": | ptr | o---+---+-----+-->| C | p | p | :\n"
|
|
": | len | 3 | : : +----+----+----+ :\n"
|
|
": | capacity | 3 | : : :\n"
|
|
": +-----------+-------+ : : :\n"
|
|
": : `- - - - - - - - - - - -'\n"
|
|
"`- - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/moves-function-calls.md:1
|
|
msgid "# Moves in Function Calls"
|
|
msgstr "# 함수 호출에서의 이동(Move)"
|
|
|
|
#: src/ownership/moves-function-calls.md:3
|
|
msgid ""
|
|
"When you pass a value to a function, the value is assigned to the function\n"
|
|
"parameter. This transfers ownership:"
|
|
msgstr "값을 함수에 전달할때, 그 값은 매개변수에 할당됩니다. 이때 소유권의 이동이 일어납니다:"
|
|
|
|
#: src/ownership/moves-function-calls.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn say_hello(name: String) {\n"
|
|
" println!(\"Hello {name}\")\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let name = String::from(\"Alice\");\n"
|
|
" say_hello(name);\n"
|
|
" // say_hello(name);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/moves-function-calls.md:20
|
|
msgid ""
|
|
"* With the first call to `say_hello`, `main` gives up ownership of `name`. Afterwards, `name` cannot be used anymore within `main`.\n"
|
|
"* The heap memory allocated for `name` will be freed at the end of the `say_hello` function.\n"
|
|
"* `main` can retain ownership if it passes `name` as a reference (`&name`) and if `say_hello` accepts a reference as a parameter.\n"
|
|
"* Alternatively, `main` can pass a clone of `name` in the first call (`name.clone()`).\n"
|
|
"* Rust makes it harder than C++ to inadvertently create copies by making move semantics the default, and by forcing programmers to make clones explicit."
|
|
msgstr ""
|
|
"* `say_hello`함수의 첫번째 호출시 `main`함수는 자신이 가진 `name`에 대한 소유권을 포기하므로, 이후 `main`함수에서는 `name`을 사용할 수 없습니다.\n"
|
|
"* `name`에 할당되있는 힙 메모리는 `say_hello`함수의 끝에서 해제됩니다.\n"
|
|
"* `main`함수에서 `name`을 참조로 전달(빌림)하고(`&name`), `say_hello`에서 매개변수를 참조형으로 수정한다면 `main`함수는 `name`의 소유권을 유지할 수 있습니다.\n"
|
|
"* 또는 첫번째 호출 시 `main`함수에서 `name`을 복제하여 전달할 수도 있습니다.(`name.clone()`)\n"
|
|
"* 러스트는 이동을 기본으로 하고 복제를 명시적으로 선언하도록 만듬으로, 의도치 않게 복사본을 만드는 것이 C++에서보다 어렵습니다."
|
|
|
|
#: src/ownership/copy-clone.md:1
|
|
msgid "# Copying and Cloning"
|
|
msgstr "# 복사(copy)와 복제(clone)"
|
|
|
|
#: src/ownership/copy-clone.md:3
|
|
msgid "While move semantics are the default, certain types are copied by default:"
|
|
msgstr "이동이 기본 설정이지만, 특정 타입은 복사됩니다:"
|
|
|
|
#: src/ownership/copy-clone.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let x = 42;\n"
|
|
" let y = x;\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" println!(\"y: {y}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/copy-clone.md:14
|
|
msgid "These types implement the `Copy` trait."
|
|
msgstr "이러한 타입들은 `Copy` 트레잇을 구현합니다."
|
|
|
|
#: src/ownership/copy-clone.md:16
|
|
msgid "You can opt-in your own types to use copy semantics:"
|
|
msgstr "직접 만든 타입들도 `Copy`트레잇을 구현하여 복사를 할 수 있습니다:"
|
|
|
|
#: src/ownership/copy-clone.md:18
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Copy, Clone, Debug)]\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1 = Point(3, 4);\n"
|
|
" let p2 = p1;\n"
|
|
" println!(\"p1: {p1:?}\");\n"
|
|
" println!(\"p2: {p2:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"#[derive(Copy, Clone, Debug)]\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1 = Point(3, 4);\n"
|
|
" let p2 = p1;\n"
|
|
" println!(\"p1: {p1:?}\");\n"
|
|
" println!(\"p2: {p2:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/ownership/copy-clone.md:30
|
|
msgid ""
|
|
"* After the assignment, both `p1` and `p2` own their own data.\n"
|
|
"* We can also use `p1.clone()` to explicitly copy the data."
|
|
msgstr ""
|
|
"* 할당 후, `p1`와 `p2`는 자신의 데이터를 소유합니다.\n"
|
|
"* 명시적으로 `p1.clone()`를 사용하여 데이터를 복사할 수 있습니다."
|
|
|
|
#: src/ownership/copy-clone.md:35
|
|
msgid "Copying and cloning are not the same thing:"
|
|
msgstr "복사(copy)와 복제(clone)는 같지 않습니다:"
|
|
|
|
#: src/ownership/copy-clone.md:37
|
|
msgid ""
|
|
"* Copying refers to bitwise copies of memory regions and does not work on arbitrary objects.\n"
|
|
"* Copying does not allow for custom logic (unlike copy constructors in C++).\n"
|
|
"* Cloning is a more general operation and also allows for custom behavior by implementing the `Clone` trait.\n"
|
|
"* Copying does not work on types that implement the `Drop` trait."
|
|
msgstr ""
|
|
"* 복사는 메모리의 내용을 그대로 한 벌 더 만드는 것을 의미하며, 아무 객체에서나 다 지원하지는 않습니다.\n"
|
|
"* 복사는 커스터마이즈 할 수 없습니다. (C++에서 복사 생성자를 통해 복사 동작을 임의로 구현할 수 있는 것과 비교가 됩니다.)\n"
|
|
"* 복제는 보다 일반적인 작업이며, `Clone`트레잇을 구현하여 복제시 동작을 커스터마이즈 할 수 있습니다.\n"
|
|
"* `Drop` 트레잇을 구현한 타입은 복사되지 않습니다."
|
|
|
|
#: src/ownership/copy-clone.md:42 src/ownership/lifetimes-function-calls.md:29
|
|
msgid "In the above example, try the following:"
|
|
msgstr "위의 예시에서 다음을 시도해 보시기 바랍니다:"
|
|
|
|
#: src/ownership/copy-clone.md:44
|
|
msgid ""
|
|
"* Add a `String` field to `struct Point`. It will not compile because `String` is not a `Copy` type.\n"
|
|
"* Remove `Copy` from the `derive` attribute. The compiler error is now in the `println!` for `p1`.\n"
|
|
"* Show that it works if you clone `p1` instead."
|
|
msgstr ""
|
|
"* `Point`구조체에 `String`필드를 추가하세요. 컴파일 되지 않을 것입니다. 왜냐하면 `String`은 `Copy`트레잇을 구현하고 있지 않기 때문입니다.\n"
|
|
"* `derive` 속성에서 `Copy`를 제거해 보세요. `p1`을 `println!` 할 때 컴파일 에러가 발생할 것입니다.\n"
|
|
"* `p1`을 복제하면 잘 동작함을 확인해 보세요."
|
|
|
|
#: src/ownership/copy-clone.md:48
|
|
msgid ""
|
|
"If students ask about `derive`, it is sufficient to say that this is a way to generate code in Rust\n"
|
|
"at compile time. In this case the default implementations of `Copy` and `Clone` traits are generated."
|
|
msgstr "만약 학생들이 `derive`에 대해 묻는다면, 컴파일 시 러스트에서 코드를 생성하는방법이라고 말하는 것으로 충분합니다. 위 경우 `Copy`와 `Clone` 트레잇에 대한 기본 구현이 생성됩니다."
|
|
|
|
#: src/ownership/borrowing.md:1
|
|
msgid "# Borrowing"
|
|
msgstr "# 빌림(Borrowing)"
|
|
|
|
#: src/ownership/borrowing.md:3
|
|
msgid ""
|
|
"Instead of transferring ownership when calling a function, you can let a\n"
|
|
"function _borrow_ the value:"
|
|
msgstr "함수 호출시 값의 소유권을 이동하는 대신의 함수가 값을 *빌려올 수* 있습니다:"
|
|
|
|
#: src/ownership/borrowing.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn add(p1: &Point, p2: &Point) -> Point {\n"
|
|
" Point(p1.0 + p2.0, p1.1 + p2.1)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1 = Point(3, 4);\n"
|
|
" let p2 = Point(10, 20);\n"
|
|
" let p3 = add(&p1, &p2);\n"
|
|
" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/borrowing.md:22
|
|
msgid ""
|
|
"* The `add` function _borrows_ two points and returns a new point.\n"
|
|
"* The caller retains ownership of the inputs."
|
|
msgstr ""
|
|
"* `add` 함수는 두 `Point` 객체 값을 _빌려_와서 새로운 `Point` 객체를 반환합니다.\n"
|
|
"* `p1`과 `p2`의 소유권은 여전히 호출자(`main`함수)에 있습니다."
|
|
|
|
#: src/ownership/borrowing.md:27
|
|
msgid "Notes on stack returns:"
|
|
msgstr "스택에 할당된 값을 리턴하는 것에 대한 참고:"
|
|
|
|
#: src/ownership/borrowing.md:28
|
|
msgid ""
|
|
"* Demonstrate that the return from `add` is cheap because the compiler can eliminate the copy operation. Change the above code to print stack addresses and run it on the [Playground]. In the \"DEBUG\" optimization level, the addresses should change, while they stay the same when changing to the \"RELEASE\" setting:\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Point(i32, i32);\n"
|
|
"\n"
|
|
" fn add(p1: &Point, p2: &Point) -> Point {\n"
|
|
" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n"
|
|
" println!(\"&p.0: {:p}\", &p.0);\n"
|
|
" p\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" let p1 = Point(3, 4);\n"
|
|
" let p2 = Point(10, 20);\n"
|
|
" let p3 = add(&p1, &p2);\n"
|
|
" println!(\"&p3.0: {:p}\", &p3.0);\n"
|
|
" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"* The Rust compiler can do return value optimization (RVO).\n"
|
|
"* In C++, copy elision has to be defined in the language specification because constructors can have side effects. In Rust, this is not an issue at all. If RVO did not happen, Rust will always performs a simple and efficient `memcpy` copy."
|
|
msgstr ""
|
|
"* `add`에서 값을 반환하는 것은 매우 값이 싸다는 것을 설명하세요. 왜냐하면, 컴파일러가 복사 과정을 생략할 수 있기 때문입니다. 위 코드를 스택 주소를 출력하도록 수정하고 [Playground]에서 수행해 보세요. \\\"디버그\\\" 최적화 레벨에서는 주소가 바뀌지만, \\\"릴리즈\\\" 레벨에서는 바뀌지 않습니다:\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Point(i32, i32);\n"
|
|
"\n"
|
|
" fn add(p1: &Point, p2: &Point) -> Point {\n"
|
|
" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n"
|
|
" println!(\"&p.0: {:p}\", &p.0);\n"
|
|
" p\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" let p1 = Point(3, 4);\n"
|
|
" let p2 = Point(10, 20);\n"
|
|
" let p3 = add(&p1, &p2);\n"
|
|
" println!(\"&p3.0: {:p}\", &p3.0);\n"
|
|
" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"* 러스트 컴파일러는 반환값 최적화(RVO)를 수행할 수 있습니다.\n"
|
|
"* C++에서 copy elision은 생성자의 부수효과 가능성이 있어 언어레벨의 정의가 필요하지만 러스트에서는 문제가 되지 않습니다. 만약 RVO가 발생하지 않으면 러스트는 항상 간단하고 효율적인 `memcpy`복사를 수행할 것입니다."
|
|
|
|
#: src/ownership/shared-unique-borrows.md:1
|
|
msgid "# Shared and Unique Borrows"
|
|
msgstr "# 공유와 고유 빌림"
|
|
|
|
#: src/ownership/shared-unique-borrows.md:3
|
|
msgid "Rust puts constraints on the ways you can borrow values:"
|
|
msgstr "러스트에서는 값을 빌릴 때 다음과 같은 제약조건이 있습니다:"
|
|
|
|
#: src/ownership/shared-unique-borrows.md:5
|
|
msgid ""
|
|
"* You can have one or more `&T` values at any given time, _or_\n"
|
|
"* You can have exactly one `&mut T` value."
|
|
msgstr ""
|
|
"* 한번에 하나 이상의 `&T` 값을 가지거나, _또는_\n"
|
|
"* 정확히 하나의 `&mut T` 값만을 가질 수 있습니다."
|
|
|
|
#: src/ownership/shared-unique-borrows.md:8
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let mut a: i32 = 10;\n"
|
|
" let b: &i32 = &a;\n"
|
|
"\n"
|
|
" {\n"
|
|
" let c: &mut i32 = &mut a;\n"
|
|
" *c = 20;\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"a: {a}\");\n"
|
|
" println!(\"b: {b}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let mut a: i32 = 10;\n"
|
|
" let b: &i32 = &a;\n"
|
|
"\n"
|
|
" {\n"
|
|
" let c: &mut i32 = &mut a;\n"
|
|
" *c = 20;\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"a: {a}\");\n"
|
|
" println!(\"b: {b}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/ownership/shared-unique-borrows.md:25
|
|
msgid ""
|
|
"* The above code does not compile because `a` is borrowed as mutable (through `c`) and as immutable (through `b`) at the same time.\n"
|
|
"* Move the `println!` statement for `b` before the scope that introduces `c` to make the code compile.\n"
|
|
"* After that change, the compiler realizes that `b` is only ever used before the new mutable borrow of `a` through `c`. This is a feature of the borrow checker called \"non-lexical lifetimes\"."
|
|
msgstr ""
|
|
"* 위 코드 컴파일 되지 않습니다. 왜냐하면 `c`는 `a`를 가변 변수로 빌렸고, 이와 동시에 `b`는 `a`를 불변 변수로 빌렸기 때문입니다.\n"
|
|
"* `b`에 대한 `println!` 구분을 `c`가 있는 스코프 앞으로 이동하면 컴파일이 됩니다.\n"
|
|
"* 이렇게 바꾸면, 컴파일러는 `c`가 `a`를 가변 변수로 빌리기 전에만 `b`가 사용된다는 것을 확인할 수 있습니다. 빌림 검사기의 이러한 기능을 \"non-lexical lifetime\" 이라고 합니다.\n"
|
|
" * 단순히 스코프만 보면 `b`의 수명은 `main`함수의 전체라고 생각할 수 있습니다. 때문에 `c`의 블록 안에서는 `a`에 대한 가변 빌림과 불변 빌림이 동시에 존재하는 것 처럼 보이며, 이는 위 제약 조건을 위반하는 것으로 생각할 수 있습니다. 그러나 컴파일러는 `b`가 `c`블록이 시작되기 전에만 사용된다는 점을 적극 활용해서 `b`의 수명을 `c`블록의 시작 전 까지로 줄입니다. 그러면 `b`와 `c`의 수명은 겹치지 않고, 따라서 제약 조건을 위반하지 않습니다."
|
|
|
|
#: src/ownership/lifetimes.md:1
|
|
msgid "# Lifetimes"
|
|
msgstr "# 수명"
|
|
|
|
#: src/ownership/lifetimes.md:3
|
|
msgid "A borrowed value has a _lifetime_:"
|
|
msgstr "빌려온 값은 *수명*을 갖습니다:"
|
|
|
|
#: src/ownership/lifetimes.md:5
|
|
msgid ""
|
|
"* The lifetime can be elided: `add(p1: &Point, p2: &Point) -> Point`.\n"
|
|
"* Lifetimes can also be explicit: `&'a Point`, `&'document str`.\n"
|
|
"* Read `&'a Point` as \"a borrowed `Point` which is valid for at least the\n"
|
|
" lifetime `a`\".\n"
|
|
"* Lifetimes are always inferred by the compiler: you cannot assign a lifetime\n"
|
|
" yourself.\n"
|
|
" * Lifetime annotations create constraints; the compiler verifies that there is\n"
|
|
" a valid solution."
|
|
msgstr ""
|
|
"* 수명은 생략할 수 있습니다: `add(p1: &Point, p2: &Point) -> Point`.\n"
|
|
"* 물론 명시할 수도 있습니다: `&'a Point`, `&'document str`.\n"
|
|
"* `&'a Point` 는 `Point`의 수명이 최소한 `'a`라는 수명보다는 같거나 더 길다는 것을 의미합니다.\n"
|
|
"* 수명은 항상 컴파일러가 자동으로 추론합니다. 직접 수명을 지정할 수는 없습니다.\n"
|
|
" * 수명 표기(`'`)은 수명 추론시 제약조건이 됩니다. 컴파일러는 이 제약조건을 만족시키는 유요한 수명을 추론할 수 있는지 검사를 합니다."
|
|
|
|
#: src/ownership/lifetimes-function-calls.md:1
|
|
msgid "# Lifetimes in Function Calls"
|
|
msgstr "# 함수 호출에서의 수명"
|
|
|
|
#: src/ownership/lifetimes-function-calls.md:3
|
|
msgid "In addition to borrowing its arguments, a function can return a borrowed value:"
|
|
msgstr "함수는 인수를 빌리는 것 외에도 빌린 값을 반환할 수 있습니다:"
|
|
|
|
#: src/ownership/lifetimes-function-calls.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n"
|
|
" if p1.0 < p2.0 { p1 } else { p2 }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1: Point = Point(10, 10);\n"
|
|
" let p2: Point = Point(20, 20);\n"
|
|
" let p3: &Point = left_most(&p1, &p2);\n"
|
|
" println!(\"left-most point: {:?}\", p3);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/lifetimes-function-calls.md:21
|
|
msgid ""
|
|
"* `'a` is a generic parameter, it is inferred by the compiler.\n"
|
|
"* Lifetimes start with `'` and `'a` is a typical default name.\n"
|
|
"* Read `&'a Point` as \"a borrowed `Point` which is valid for at least the\n"
|
|
" lifetime `a`\".\n"
|
|
" * The _at least_ part is important when parameters are in different scopes."
|
|
msgstr ""
|
|
"* `'a`는 제네릭 매개변수로 컴파일러로에 의해 추론됩니다.\n"
|
|
"* 수명의 이름은 `'` 로 시작하며 보통 `'a`를 많이 씁니다.\n"
|
|
"* `&'a Point` 는 `Point`의 수명이 `'a` 수명과 최소한 같거나 더 길다는 것을 의미합니다.\n"
|
|
" * 매개변수들이 서로 다른 스코프에 있을 경우 \"최소한\"이라는 조건이 중요합니다."
|
|
|
|
#: src/ownership/lifetimes-function-calls.md:31
|
|
msgid ""
|
|
"* Move the declaration of `p2` and `p3` into a a new scope (`{ ... }`), resulting in the following code:\n"
|
|
" ```rust,ignore\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Point(i32, i32);\n"
|
|
"\n"
|
|
" fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n"
|
|
" if p1.0 < p2.0 { p1 } else { p2 }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" let p1: Point = Point(10, 10);\n"
|
|
" let p3: &Point;\n"
|
|
" {\n"
|
|
" let p2: Point = Point(20, 20);\n"
|
|
" p3 = left_most(&p1, &p2);\n"
|
|
" }\n"
|
|
" println!(\"left-most point: {:?}\", p3);\n"
|
|
" }\n"
|
|
" ```\n"
|
|
" Note how this does not compile since `p3` outlives `p2`.\n"
|
|
"\n"
|
|
"* Reset the workspace and change the function signature to `fn left_most<'a, 'b>(p1: &'a Point, p2: &'a Point) -> &'b Point`. This will not compile because the relationship between the lifetimes `'a` and `'b` is unclear.\n"
|
|
"* Another way to explain it:\n"
|
|
" * Two references to two values are borrowed by a function and the function returns\n"
|
|
" another reference.\n"
|
|
" * It must have come from one of those two inputs (or from a global variable).\n"
|
|
" * Which one is it? The compiler needs to to know, so at the call site the returned reference is not used\n"
|
|
" for longer than a variable from where the reference came from."
|
|
msgstr ""
|
|
"* `p2`와 `p3`를 새로운 범위(`{...}`)로 아래 코드와 같이 이동해 봅니다:\n"
|
|
" ```rust,ignore\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Point(i32, i32);\n"
|
|
"\n"
|
|
" fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n"
|
|
" if p1.0 < p2.0 { p1 } else { p2 }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" let p1: Point = Point(10, 10);\n"
|
|
" let p3: &Point;\n"
|
|
" {\n"
|
|
" let p2: Point = Point(20, 20);\n"
|
|
" p3 = left_most(&p1, &p2);\n"
|
|
" }\n"
|
|
" println!(\"left-most point: {:?}\", p3);\n"
|
|
" }\n"
|
|
" ```\n"
|
|
" `p3`의 수명이 `p2` 보다 길기 때문에 이 예제는 컴파일되지 않음을 확인하시기 바랍니다.\n"
|
|
"\n"
|
|
"* 작업공간을 초기화 한 후 함수 시그니처를 `fn left_most<'a, 'b>(p1: &'a Point, p2: &'a Point) -> &'b Point`로 변경해 봅니다. 이 경우 `'a`와 `'b`사이의 관계가 불분명하기 때문에 컴파일 되지 않습니다.\n"
|
|
"* 이 에러를 설명하는 또 다른 방법은 다음과 같습니다:\n"
|
|
" * 이 함수는 두 값을 빌려서, 새로운 참조를 반환합니다.\n"
|
|
" * 이 반환된 참조는 두 입력 중 하나로 부터 와야 합니다. (아니면 전역 변수로 부터)\n"
|
|
" * 두 입력 중 어떤 것일까요? 컴파일러는 이를 알아야 합니다. 그래야만 함수 호출부에서 봤을 때, 반환된 참조의 수명이 원래 값을 수명보다 길지 않음을 확인할 수 있기 때문입니다."
|
|
|
|
#: src/ownership/lifetimes-data-structures.md:1
|
|
msgid "# Lifetimes in Data Structures"
|
|
msgstr "# 구조체에서의 수명"
|
|
|
|
#: src/ownership/lifetimes-data-structures.md:3
|
|
msgid "If a data type stores borrowed data, it must be annotated with a lifetime:"
|
|
msgstr "어떤 타입이 빌려온 데이터를 저장하고 있다면, 반드시 수명을 표시해야 합니다:"
|
|
|
|
#: src/ownership/lifetimes-data-structures.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Highlight<'doc>(&'doc str);\n"
|
|
"\n"
|
|
"fn erase(text: String) {\n"
|
|
" println!(\"Bye {text}!\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let text = String::from(\"The quick brown fox jumps over the lazy dog.\");\n"
|
|
" let fox = Highlight(&text[4..19]);\n"
|
|
" let dog = Highlight(&text[35..43]);\n"
|
|
" // erase(text);\n"
|
|
" println!(\"{fox:?}\");\n"
|
|
" println!(\"{dog:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/ownership/lifetimes-data-structures.md:25
|
|
msgid ""
|
|
"* In the above example, the annotation on `Highlight` enforces that the data underlying the contained `&str` lives at least as long as any instance of `Highlight` that uses that data.\n"
|
|
"* If `text` is consumed before the end of the lifetime of `fox` (or `dog`), the borrow checker throws an error.\n"
|
|
"* Types with borrowed data force users to hold on to the original data. This can be useful for creating lightweight views, but it generally makes them somewhat harder to use.\n"
|
|
"* When possible, make data structures own their data directly.\n"
|
|
"* Some structs with multiple references inside can have more than one lifetime annotation. This can be necessary if there is a need to describe lifetime relationships between the references themselves, in addition to the lifetime of the struct itself. Those are very advanced use cases."
|
|
msgstr ""
|
|
"* 위의 예제에서 `Highlight`의 어노테이션(`<'doc>`)은 적어도 `Highlight` 인스턴스가 살아있는 동안에는 그 내부의 `&str`가 가리키는 데이터 역시 살아있어야 한다는 것을 의미합니다.\n"
|
|
"* 만약 `text`가 `fox` (혹은 `dog`)의 수명이 다하기 전에 `erase`함수 호출 등으로 사라지게 된다면 빌림 검사기가 에러를 발생합니다.\n"
|
|
"* 빌린 데이터를 가지고 있는 타입은 사용자로 하여금 원본 데이터를 유지하도록 강제합니다. 이런 타입은 경량 뷰(lightweight view)를 만드는데 유용하지만, 이 제약 조건 때문에 이런 타입을 사용하는 것이 쉽지만은 않습니다.\n"
|
|
"* 따라서, 가능하다면, 구조체가 자신의 데이터를 직접 소유하도록 하는 것이 좋습니다.\n"
|
|
"* 한 구조체안에 여러 참조가 있으면서, 이 참조들의 수명이 서로 다르게 지정되는 경우도 있습니다. 이는 참조와 그 구조체 간의 관계 뿐만이 아니라, 그 참조들 사이의 수명 관계를 설명해야 할 경우에 필요합니다. 매우 고급 기술입니다."
|
|
|
|
#: src/exercises/day-1/afternoon.md:1
|
|
msgid "# Day 1: Afternoon Exercises"
|
|
msgstr "# 1일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-1/afternoon.md:3
|
|
msgid "We will look at two things:"
|
|
msgstr "이번 연습문제는 아래 두가지입니다:"
|
|
|
|
#: src/exercises/day-1/afternoon.md:5
|
|
msgid ""
|
|
"* A small book library,\n"
|
|
"\n"
|
|
"* Iterators and ownership (hard)."
|
|
msgstr ""
|
|
"* 작은 도서관\n"
|
|
"\n"
|
|
"* 반복자와 소유권 (어려움)"
|
|
|
|
#: src/exercises/day-1/book-library.md:1
|
|
msgid "# Designing a Library"
|
|
msgstr "# 도서관 설계"
|
|
|
|
#: src/exercises/day-1/book-library.md:3
|
|
msgid ""
|
|
"We will learn much more about structs and the `Vec<T>` type tomorrow. For now,\n"
|
|
"you just need to know part of its API:"
|
|
msgstr "우리는 내일 구조체와 `Vec<T>`에 대해 더 많은 것을 배울 것입니다. 일단 오늘은 API의 일부만 알면 됩니다:"
|
|
|
|
#: src/exercises/day-1/book-library.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut vec = vec![10, 20];\n"
|
|
" vec.push(30);\n"
|
|
" println!(\"middle value: {}\", vec[vec.len() / 2]);\n"
|
|
" for item in vec.iter() {\n"
|
|
" println!(\"item: {item}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/book-library.md:17
|
|
msgid ""
|
|
"Use this to create a library application. Copy the code below to\n"
|
|
"<https://play.rust-lang.org/> and update the types to make it compile:"
|
|
msgstr "도서관 프로그램을 만들기 위해 아래 코드를 <https://play.rust-lang.org/>에 복사해서 구현하시면 됩니다:"
|
|
|
|
#: src/exercises/day-1/book-library.md:20
|
|
msgid ""
|
|
"```rust,should_panic\n"
|
|
"\n"
|
|
"struct Library {\n"
|
|
" books: Vec<Book>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"struct Book {\n"
|
|
" title: String,\n"
|
|
" year: u16,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Book {\n"
|
|
" // This is a constructor, used below.\n"
|
|
" fn new(title: &str, year: u16) -> Book {\n"
|
|
" Book {\n"
|
|
" title: String::from(title),\n"
|
|
" year,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// This makes it possible to print Book values with {}.\n"
|
|
"impl std::fmt::Display for Book {\n"
|
|
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n"
|
|
" write!(f, \"{} ({})\", self.title, self.year)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Library {\n"
|
|
" fn new() -> Library {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" //fn len(self) -> usize {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
"\n"
|
|
" //fn is_empty(self) -> bool {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
"\n"
|
|
" //fn add_book(self, book: Book) {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
"\n"
|
|
" //fn print_books(self) {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
"\n"
|
|
" //fn oldest_book(self) -> Option<&Book> {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
"}\n"
|
|
"\n"
|
|
"// This shows the desired behavior. Uncomment the code below and\n"
|
|
"// implement the missing methods. You will need to update the\n"
|
|
"// method signatures, including the \"self\" parameter! You may\n"
|
|
"// also need to update the variable bindings within main.\n"
|
|
"fn main() {\n"
|
|
" let library = Library::new();\n"
|
|
"\n"
|
|
" //println!(\"Our library is empty: {}\", library.is_empty());\n"
|
|
"\n"
|
|
" let favorite_book = Book::new(\"Lord of the Rings\", 1954);\n"
|
|
" println!(\"Our favorite book {favorite_book} should go in the library\");\n"
|
|
" //library.add_book(favorite_book);\n"
|
|
" //library.add_book(Book::new(\"Alice's Adventures in Wonderland\", 1865));\n"
|
|
" //\n"
|
|
" //library.print_books();\n"
|
|
" //\n"
|
|
" //match library.oldest_book() {\n"
|
|
" // Some(book) => println!(\"My oldest book is {book}\"),\n"
|
|
" // None => println!(\"My library is empty!\"),\n"
|
|
" //}\n"
|
|
" //\n"
|
|
" //println!(\"Our library has {} books\", library.len());\n"
|
|
" for book in library.books {\n"
|
|
" println!(\"{book}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/book-library.md:104
|
|
msgid "[Solution](solutions-afternoon.md#designing-a-library)"
|
|
msgstr "[해답](solutions-afternoon.md#designing-a-library)"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:1
|
|
msgid "# Iterators and Ownership"
|
|
msgstr "# 반복자와 소유권"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:3
|
|
msgid ""
|
|
"The ownership model of Rust affects many APIs. An example of this is the\n"
|
|
"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and\n"
|
|
"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html)\n"
|
|
"traits."
|
|
msgstr "러스트의 소유권 모델은 많은 API에 반영이 되어 있습니다. 예를들어 [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) 와 [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) 같은 트레잇이 있습니다."
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:8
|
|
msgid "## `Iterator`"
|
|
msgstr "## `Iterator`"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:10
|
|
msgid ""
|
|
"Traits are like interfaces: they describe behavior (methods) for a type. The\n"
|
|
"`Iterator` trait simply says that you can call `next` until you get `None` back:"
|
|
msgstr "트레잇은 타입에 대한 행동(메서드)를 설명한다는 점에서 인터페이스와 유사합니다. `Iterator`는 단순히 `None`이 나올때까지 `next`를 호출하는 것이 가능하다는 것을 나타내는 트레잇입니다:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:13
|
|
msgid ""
|
|
"```rust\n"
|
|
"pub trait Iterator {\n"
|
|
" type Item;\n"
|
|
" fn next(&mut self) -> Option<Self::Item>;\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:20
|
|
msgid "You use this trait like this:"
|
|
msgstr "`Iterator` 트레잇은 이렇게 사용합니다:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:22
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v: Vec<i8> = vec![10, 20, 30];\n"
|
|
" let mut iter = v.iter();\n"
|
|
"\n"
|
|
" println!(\"v[0]: {:?}\", iter.next());\n"
|
|
" println!(\"v[1]: {:?}\", iter.next());\n"
|
|
" println!(\"v[2]: {:?}\", iter.next());\n"
|
|
" println!(\"No more items: {:?}\", iter.next());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:34
|
|
msgid "What is the type returned by the iterator? Test your answer here:"
|
|
msgstr "반복자가 반환하는 값들은 타입이 뭘까요? 여기서 답을 테스트 해 보세요:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:36
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let v: Vec<i8> = vec![10, 20, 30];\n"
|
|
" let mut iter = v.iter();\n"
|
|
"\n"
|
|
" let v0: Option<..> = iter.next();\n"
|
|
" println!(\"v0: {v0:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let v: Vec<i8> = vec![10, 20, 30];\n"
|
|
" let mut iter = v.iter();\n"
|
|
"\n"
|
|
" let v0: Option<..> = iter.next();\n"
|
|
" println!(\"v0: {v0:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:46
|
|
msgid "Why is this type used?"
|
|
msgstr "왜 이런 타입이 사용되는 것일까요?"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:48
|
|
msgid "## `IntoIterator`"
|
|
msgstr "## `IntoIterator`"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:50
|
|
msgid ""
|
|
"The `Iterator` trait tells you how to _iterate_ once you have created an\n"
|
|
"iterator. The related trait `IntoIterator` tells you how to create the iterator:"
|
|
msgstr "`Iterator` 트레잇은 생성된 반복자를 *사용*하는 방법을 알려줍니다. 반면 `IntoIterator` 트레잇은 반복자를 *생성*하는 방법을 알려줍니다:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:53
|
|
msgid ""
|
|
"```rust\n"
|
|
"pub trait IntoIterator {\n"
|
|
" type Item;\n"
|
|
" type IntoIter: Iterator<Item = Self::Item>;\n"
|
|
"\n"
|
|
" fn into_iter(self) -> Self::IntoIter;\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:62
|
|
msgid ""
|
|
"The syntax here means that every implementation of `IntoIterator` must\n"
|
|
"declare two types:"
|
|
msgstr "`IntoIterator`의 모든 구현은 반드시 다음의 두 타입을 선언해야합니다:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:65
|
|
msgid ""
|
|
"* `Item`: the type we iterate over, such as `i8`,\n"
|
|
"* `IntoIter`: the `Iterator` type returned by the `into_iter` method."
|
|
msgstr ""
|
|
"* `Item`: `i8`과 같이 반복되는 값의 타입\n"
|
|
"* `IntoIter`: `into_iter` 메서드에서 반환되는 `Iterator`타입."
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:68
|
|
msgid ""
|
|
"Note that `IntoIter` and `Item` are linked: the iterator must have the same\n"
|
|
"`Item` type, which means that it returns `Option<Item>`"
|
|
msgstr "`IntoIter`에는 `Item`이 연결되어 있음을 주목하세요. `IntoIter` 반복자는 `Item` 타입의 데이터를 가리켜야 합니다. 즉, 반복자는 `Option<Item>`을 리턴합니다."
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:71
|
|
msgid "Like before, what is the type returned by the iterator?"
|
|
msgstr "이전과 마찬가지로, 반복자가 반환하는 타입은 무엇입니까?"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:73
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let v: Vec<String> = vec![String::from(\"foo\"), String::from(\"bar\")];\n"
|
|
" let mut iter = v.into_iter();\n"
|
|
"\n"
|
|
" let v0: Option<..> = iter.next();\n"
|
|
" println!(\"v0: {v0:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:83
|
|
msgid "## `for` Loops"
|
|
msgstr "## 배열과 `for` 반복문"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:85
|
|
msgid ""
|
|
"Now that we know both `Iterator` and `IntoIterator`, we can build `for` loops.\n"
|
|
"They call `into_iter()` on an expression and iterates over the resulting\n"
|
|
"iterator:"
|
|
msgstr "자, 이제 우리는 `Iterator`와 `IntoIterator`를 알았으므로 `for` 루프를 만들 수 있습니다. `for` 루프는 `into_iter()`를 호출하여 반복자를 만든 다음 그 반복자를 이용하여 요소들을 반복해서 접근합니다:"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:89
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v: Vec<String> = vec![String::from(\"foo\"), String::from(\"bar\")];\n"
|
|
"\n"
|
|
" for word in &v {\n"
|
|
" println!(\"word: {word}\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" for word in v {\n"
|
|
" println!(\"word: {word}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:103
|
|
msgid "What is the type of `word` in each loop?"
|
|
msgstr "매 루프에서 `word`의 타입은 무엇입니까?"
|
|
|
|
#: src/exercises/day-1/iterators-and-ownership.md:105
|
|
msgid ""
|
|
"Experiment with the code above and then consult the documentation for [`impl\n"
|
|
"IntoIterator for\n"
|
|
"&Vec<T>`](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-IntoIterator-for-%26%27a%20Vec%3CT%2C%20A%3E)\n"
|
|
"and [`impl IntoIterator for\n"
|
|
"Vec<T>`](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-IntoIterator-for-Vec%3CT%2C%20A%3E)\n"
|
|
"to check your answers."
|
|
msgstr "위 코드에서 실험 해 본 후 다음 문서를 참조해서 답변을 확인하시기 바랍니다: [`impl IntoIterator for &Vec<T>`](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-IntoIterator-for-%26%27a%20Vec%3CT%2C%20A%3E), [`impl IntoIterator for Vec<T>`](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-IntoIterator-for-Vec%3CT%2C%20A%3E)"
|
|
|
|
#: src/welcome-day-2.md:1
|
|
msgid "# Welcome to Day 2"
|
|
msgstr "# 2일차 개요"
|
|
|
|
#: src/welcome-day-2.md:3
|
|
msgid "Now that we have seen a fair amount of Rust, we will continue with:"
|
|
msgstr "상당한 분량의 러스트에 대해 보았고, 이어서 오늘 강의를 진행하겠습니다:"
|
|
|
|
#: src/welcome-day-2.md:5
|
|
msgid ""
|
|
"* Structs, enums, methods.\n"
|
|
"\n"
|
|
"* Pattern matching: destructuring enums, structs, and arrays.\n"
|
|
"\n"
|
|
"* Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and\n"
|
|
" `continue`.\n"
|
|
"\n"
|
|
"* The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, `Rc`\n"
|
|
" and `Arc`.\n"
|
|
"\n"
|
|
"* Modules: visibility, paths, and filesystem hierarchy."
|
|
msgstr ""
|
|
"* 구조체, 열거형, 메서드.\n"
|
|
"\n"
|
|
"* 패턴 매칭: 열거형, 구조체 그리고 배열 분해.\n"
|
|
"\n"
|
|
"* 흐름 제어: `if`, `if let`, `while`, `while let`, `break`, 그리고 `continue`.\n"
|
|
"\n"
|
|
"* 표준 라이브러리: `String`, `Option` 과 `Result`, `Vec`, `HashMap`, `Rc` 그리고 `Arc`.\n"
|
|
"\n"
|
|
"* 모듈: 가시성, 경로 및 파일 시스템 계층."
|
|
|
|
#: src/structs.md:1
|
|
msgid "# Structs"
|
|
msgstr "# 구조체"
|
|
|
|
#: src/structs.md:3
|
|
msgid "Like C and C++, Rust has support for custom structs:"
|
|
msgstr "C/C++ 와 마찬가지로 러스트는 커스텀 구조체를 지원합니다:"
|
|
|
|
#: src/structs.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut peter = Person {\n"
|
|
" name: String::from(\"Peter\"),\n"
|
|
" age: 27,\n"
|
|
" };\n"
|
|
" println!(\"{} is {} years old\", peter.name, peter.age);\n"
|
|
" \n"
|
|
" peter.age = 28;\n"
|
|
" println!(\"{} is {} years old\", peter.name, peter.age);\n"
|
|
" \n"
|
|
" let jackie = Person {\n"
|
|
" name: String::from(\"Jackie\"),\n"
|
|
" ..peter\n"
|
|
" };\n"
|
|
" println!(\"{} is {} years old\", jackie.name, jackie.age);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/structs.md:29
|
|
msgid ""
|
|
"<details>\n"
|
|
"Key Points: "
|
|
msgstr ""
|
|
"<details>\n"
|
|
"키 포인트: "
|
|
|
|
#: src/structs.md:32
|
|
msgid ""
|
|
"* Structs work like in C or C++.\n"
|
|
" * Like in C++, and unlike in C, no typedef is needed to define a type.\n"
|
|
" * Unlike in C++, there is no inheritance between structs.\n"
|
|
"* Methods are defined in an `impl` block, which we will see in following slides.\n"
|
|
"* This may be a good time to let people know there are different types of structs. \n"
|
|
" * Zero-sized structs `e.g., struct Foo;` might be used when implementing a trait on some type but don’t have any data that you want to store in the value itself. \n"
|
|
" * The next slide will introduce Tuple structs, used when the field names are not important.\n"
|
|
"* The syntax `..peter` allows us to copy the majority of the fields from the old struct without having to explicitly type it all out. It must always be the last element."
|
|
msgstr ""
|
|
"* 구조체는 C/C++ 와 유사합니다.\n"
|
|
" * C++ 와 같지만 C와는 달리 타입을 정의하기 위해 'typedef'가 필요하지 않습니다.\n"
|
|
" * C++ 와 달리 구조체 간 상속은 없습니다.\n"
|
|
"* 메서드는 `impl`블록에 정의 합니다. 다음 슬라이드에서 확인 할 수 있습니다.\n"
|
|
"* 사람들에게 다른 종류의 구조체가 있음을 알게 하기에 좋은 시간일 것입니다.\n"
|
|
" * 0 크기 구조체(예: `struct Foo;`)는 데이터를 가지고 있지 않지만 특정 타입의 트레잇을 구현할 때 유용합니다.\n"
|
|
" * 다음 슬라이드에서는 필드 이름이 덜 중요할 때 사용할 수 있는 튜플 구조체를 소개합니다.\n"
|
|
"* `..peter` 문법은 한 구조체에서 다른 구조체로 대부분의 값을 복사하려고 하는 경우에 하나하나 타이핑하는 수고를 덜어줍니다. 반드시 맨 마지막에 와야 합니다."
|
|
|
|
#: src/structs/tuple-structs.md:1
|
|
msgid "# Tuple Structs"
|
|
msgstr "# 튜플"
|
|
|
|
#: src/structs/tuple-structs.md:3
|
|
msgid "If the field names are unimportant, you can use a tuple struct:"
|
|
msgstr "각 필드 이름이 중요하지 않다면 튜플 구조체를 사용할 수 있습니다:"
|
|
|
|
#: src/structs/tuple-structs.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p = Point(17, 23);\n"
|
|
" println!(\"({}, {})\", p.0, p.1);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"struct Point(i32, i32);\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p = Point(17, 23);\n"
|
|
" println!(\"({}, {})\", p.0, p.1);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/structs/tuple-structs.md:14
|
|
msgid "This is often used for single-field wrappers (called newtypes):"
|
|
msgstr "튜플 구조체는 종종 단일 필드의 래퍼(wrapper, 러스트에서 뉴타입(newtype)이라고 부름)로 사용됩니다:"
|
|
|
|
#: src/structs/tuple-structs.md:16
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"struct PoundOfForce(f64);\n"
|
|
"struct Newtons(f64);\n"
|
|
"\n"
|
|
"fn compute_thruster_force() -> PoundOfForce {\n"
|
|
" todo!(\"Ask a rocket scientist at NASA\")\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn set_thruster_force(force: Newtons) {\n"
|
|
" // ...\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let force = compute_thruster_force();\n"
|
|
" set_thruster_force(force);\n"
|
|
"}\n"
|
|
"\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/structs/tuple-structs.md:37
|
|
msgid ""
|
|
"* Newtypes are a great way to encode additional information about the value in a primitive type, for example:\n"
|
|
" * The number is measured in some units: `Newtons` in the example above.\n"
|
|
" * The value passed some validation when it was created, so you no longer have to validate it again at every use: 'PhoneNumber(String)` or `OddNumber(u32)`.\n"
|
|
"* Demonstrate how to add a `f64` value to a `Newtons` type by accessing the single field in the newtype.\n"
|
|
" * Rust generally doesn’t like inexplicit things, like automatic unwrapping or for instance using booleans as integers.\n"
|
|
" * Operator overloading is discussed on Day 3 (generics). "
|
|
msgstr ""
|
|
"* 뉴타입은 다음과 같은 원시타입 값에 특별한 의미를 부여하는 데 유용합니다.\n"
|
|
" * 단위 표시를 위한 숫자: 위 예제에서는 뉴턴 단위 표기를 위해 사용합니다.\n"
|
|
" * 값이 생성될 때 이미 유효성 검사를 통과 했으므로 추가적인 검사가 필요없는 경우: `PhoneNumber(String)`또는 `OddNumber(u32)`\n"
|
|
"* `Newtons` 타입의 값에 `f64` 값을 더하는 방법을 보여주세요.\n"
|
|
" * 러스트는 대체로 분명하지 않은 것을 싫어합니다. 예를 들면 자동으로 unwrap하거나 불리언 값을 정수 값으로 사용하는 것들이 그렇습니다.\n"
|
|
" * 연산자 재정의는 3일차 제네릭 부분에서 다룹니다."
|
|
|
|
#: src/structs/field-shorthand.md:1
|
|
msgid "# Field Shorthand Syntax"
|
|
msgstr "# 필드 할당 단축 문법(Field Shorthand Syntax)"
|
|
|
|
#: src/structs/field-shorthand.md:3
|
|
msgid ""
|
|
"If you already have variables with the right names, then you can create the\n"
|
|
"struct using a shorthand:"
|
|
msgstr "구조체 필드와 동일한 이름의 변수가 있다면 아래와 같이 \"짧은 문법\"으로 구조체를 생성할 수 있습니다:"
|
|
|
|
#: src/structs/field-shorthand.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Person {\n"
|
|
" fn new(name: String, age: u8) -> Person {\n"
|
|
" Person { name, age }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let peter = Person::new(String::from(\"Peter\"), 27);\n"
|
|
" println!(\"{peter:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/structs/field-shorthand.md:27
|
|
msgid ""
|
|
"* The `new` function could be written using `Self` as a type, as it is interchangeable with the struct type name\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
" }\n"
|
|
" impl Person {\n"
|
|
" fn new(name: String, age: u8) -> Self {\n"
|
|
" Self { name, age }\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ``` \n"
|
|
"* Implement the `Default` trait for the struct. Define some fields and use the default values for the other fields.\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
" }\n"
|
|
" impl Default for Person {\n"
|
|
" fn default() -> Person {\n"
|
|
" Person {\n"
|
|
" name: \"Bot\".to_string(),\n"
|
|
" age: 0,\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
" fn create_default() {\n"
|
|
" let tmp = Person {\n"
|
|
" ..Default::default()\n"
|
|
" };\n"
|
|
" let tmp = Person {\n"
|
|
" name: \"Sam\".to_string(),\n"
|
|
" ..Default::default()\n"
|
|
" };\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
"* Methods are defined in the `impl` block.\n"
|
|
"* Use struct update syntax to define a new structure using `peter`. Note that the variable `peter` will no longer be accessible afterwards.\n"
|
|
"* Use `{:#?}` when printing structs to request the `Debug` representation."
|
|
msgstr ""
|
|
"* `new`함수를 다음처럼 구조체 이름 대신 `Self`를 사용하여 작성해도 됩니다.\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
" }\n"
|
|
" impl Person {\n"
|
|
" fn new(name: String, age: u8) -> Self {\n"
|
|
" Self { name, age }\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ``` \n"
|
|
"* `Default` 트레잇을 구현해보세요. 필드 몇개는 초기화하고 나머지 필드는 디폴트 값을 사용할 수 있습니다.\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #[derive(Debug)]\n"
|
|
" struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
" }\n"
|
|
" impl Default for Person {\n"
|
|
" fn default() -> Person {\n"
|
|
" Person {\n"
|
|
" name: \"Bot\".to_string(),\n"
|
|
" age: 0,\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
" fn create_default() {\n"
|
|
" let tmp = Person {\n"
|
|
" ..Default::default()\n"
|
|
" };\n"
|
|
" let tmp = Person {\n"
|
|
" name: \"Sam\".to_string(),\n"
|
|
" ..Default::default()\n"
|
|
" };\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
"* 메서드는 `impl` 블록에 정의됩니다.\n"
|
|
"* `peter`와 구조체 업데이트 문법을 사용하여 새로운 구조체 인스턴스를 만들어보세요. 이때, `peter`는 더이상 사용할 수 없게 됩니다.\n"
|
|
"* 구조체를 `Debug` 형태로 출력하려면 `{:#?}`를 사용하세요."
|
|
|
|
#: src/enums.md:1
|
|
msgid "# Enums"
|
|
msgstr "# 열거형"
|
|
|
|
#: src/enums.md:3
|
|
msgid ""
|
|
"The `enum` keyword allows the creation of a type which has a few\n"
|
|
"different variants:"
|
|
msgstr "`enum` 키워드는 몇가지 유형(variant)으로 표현되는 타입을 생성합니다:"
|
|
|
|
#: src/enums.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn generate_random_number() -> i32 {\n"
|
|
" 4 // Chosen by fair dice roll. Guaranteed to be random.\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"enum CoinFlip {\n"
|
|
" Heads,\n"
|
|
" Tails,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn flip_coin() -> CoinFlip {\n"
|
|
" let random_number = generate_random_number();\n"
|
|
" if random_number % 2 == 0 {\n"
|
|
" return CoinFlip::Heads;\n"
|
|
" } else {\n"
|
|
" return CoinFlip::Tails;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"You got: {:?}\", flip_coin());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/enums.md:33 src/enums/sizes.md:29 src/methods.md:30
|
|
#: src/methods/example.md:46 src/pattern-matching.md:25
|
|
#: src/pattern-matching/match-guards.md:22 src/control-flow/blocks.md:42
|
|
msgid "Key Points:"
|
|
msgstr "키 포인트:"
|
|
|
|
#: src/enums.md:35
|
|
msgid ""
|
|
"* Enumerations allow you to collect a set of values under one type\n"
|
|
"* This page offers an enum type `CoinFlip` with two variants `Heads` and `Tail`. You might note the namespace when using variants.\n"
|
|
"* This might be a good time to compare Structs and Enums:\n"
|
|
" * In both, you can have a simple version without fields (unit struct) or one with different types of fields (variant payloads). \n"
|
|
" * In both, associated functions are defined within an `impl` block.\n"
|
|
" * You could even implement the different variants of an enum with separate structs but then they wouldn’t be the same type as they would if they were all defined in an enum. "
|
|
msgstr ""
|
|
"* 열거형은 값들의 집합을 하나의 타입으로 표현할 수 있게 합니다.\n"
|
|
"* 위의 `CoinFlip` 열거형 타입은 `Heads`와 `Tail` 두가지 variant를 가집니다. 열거형 타입의 variant는 네임스페이스를 붙여서 사용합니다.\n"
|
|
"* 구조체와 열거형을 비교해 볼까요?\n"
|
|
" * 구조체나 열거형 모두, 필드가 하나도 없는 단순한 형태도 가능 하고, 여러 타입의 필드를 가질 수도 있습니다.\n"
|
|
" * 둘 다 연관함수를 `impl`블록으로 정의 할 수 있습니다.\n"
|
|
" * 열거형 타입의 각 variant를 별도의 구조체로 정의할 수도 있지만, 그러면 열거형을 사용했을 때처럼 하나의 타입으로 취급할 수 없습니다."
|
|
|
|
#: src/enums/variant-payloads.md:1
|
|
msgid "# Variant Payloads"
|
|
msgstr "# 데이터를 포함하는 열거형(Variant Payloads)"
|
|
|
|
#: src/enums/variant-payloads.md:3
|
|
msgid ""
|
|
"You can define richer enums where the variants carry data. You can then use the\n"
|
|
"`match` statement to extract the data from each variant:"
|
|
msgstr "좀더 복잡한 열거형의 경우 variant에 데이터(payload)를 포함시키도 합니다. 각 variant에 담긴 데이터는 `match`문을 이용해 추출합니다:"
|
|
|
|
#: src/enums/variant-payloads.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"enum WebEvent {\n"
|
|
" PageLoad, // Variant without payload\n"
|
|
" KeyPress(char), // Tuple struct variant\n"
|
|
" Click { x: i64, y: i64 }, // Full struct variant\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn inspect(event: WebEvent) {\n"
|
|
" match event {\n"
|
|
" WebEvent::PageLoad => println!(\"page loaded\"),\n"
|
|
" WebEvent::KeyPress(c) => println!(\"pressed '{c}'\"),\n"
|
|
" WebEvent::Click { x, y } => println!(\"clicked at x={x}, y={y}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let load = WebEvent::PageLoad;\n"
|
|
" let press = WebEvent::KeyPress('x');\n"
|
|
" let click = WebEvent::Click { x: 20, y: 80 };\n"
|
|
"\n"
|
|
" inspect(load);\n"
|
|
" inspect(press);\n"
|
|
" inspect(click);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/enums/variant-payloads.md:35
|
|
msgid ""
|
|
"* The values in the enum variants can only be accessed after being pattern matched. The pattern binds references to the fields in the \"match arm\" after the `=>`.\n"
|
|
" * The expression is matched against the patterns from top to bottom. There is no fall-through like in C or C++.\n"
|
|
" * The match expression has a value. The value is the last expression in the match arm which was executed.\n"
|
|
" * Starting from the top we look for what pattern matches the value then run the code following the arrow. Once we find a match, we stop. \n"
|
|
"* Demonstrate what happens when the search is inexhaustive. Note the advantage the Rust compiler provides by confirming when all cases are handled. \n"
|
|
"* `match` inspects a hidden discriminant field in the `enum`.\n"
|
|
"* It is possible to retrieve the discriminant by calling `std::mem::discriminant()`\n"
|
|
" * This is useful, for example, if implementing `PartialEq` for structs where comparing field values doesn't affect equality.\n"
|
|
"* `WebEvent::Click { ... }` is not exactly the same as `WebEvent::Click(Click)` with a top level `struct Click { ... }`. The inlined version cannot implement traits, for example. \n"
|
|
" "
|
|
msgstr ""
|
|
"* 열거형 안의 값은 패턴 매칭이 되고 난 이후에만 접근 가능합니다. 그 값에 대한 레퍼런스는 `=>` 이후에 사용가능합니다.\n"
|
|
" * 매치 패턴들은 위에서 아래로 순서에 따라 검사합니다. C나 C++에서와 같은 fall-through는 없습니다.\n"
|
|
" * 매치 표현식 자체는 값을 가집니다. 그 값은 매칭이 된 패턴에서 가장 마지막에 수행된 표현식이 됩니다.\n"
|
|
" * 가장 위에서 부터 어떤 패턴이 주어진 값과 매칭하는지 검사한 다음, 매칭된 것이 발견되면 화살표를 따라 코드를 수행합니다. 한 번 매칭이 되고 코드가 수행이 되면, 더이상의 매칭은 없습니다.\n"
|
|
"* 매칭 패턴들이 불충분 하다면 어떤 일이 일어나는지 설명하세요. 러스트 컴파일러는 모든 가능한 케이스들이 핸들링 되는지 체크한다는 점을 상기시키세요.\n"
|
|
"* `match`는 주어진 열거형 값이 실제로 어떤 variant인지 판단하기 위해, 그 variant의 종류가 기록된, 숨겨진 필드(식별자)의 값을 검사합니다.\n"
|
|
"* `std::mem::discriminant()`를 이용하여 식별자를 얻을 수도 있습니다.\n"
|
|
" * 이는 각 필드 값을 굳이 비교할 필요 없는 구조체에 대해 `PartialEq` 트레잇을 구현할 때 유용합니다.\n"
|
|
"* `WebEvent::Click { ... }`은 최상위 레벨 구조체 `struct Click {...}`를 따로 정의하고 `WebEvent::Click(Click)`처럼 튜플 형태로 정의한 것과 정확히 같진 않습니다. 예를 들어 `WebEvent::Click { ... }` 로 정의한 경우, 구조체 형태와 유사하지만 트레잇을 구현 할 수 없습니다.\n"
|
|
" "
|
|
|
|
#: src/enums/sizes.md:1
|
|
msgid "# Enum Sizes"
|
|
msgstr "# 열거형 타입의 크기"
|
|
|
|
#: src/enums/sizes.md:3
|
|
msgid "Rust enums are packed tightly, taking constraints due to alignment into account:"
|
|
msgstr "러스트의 열거형은 정렬(alignment)로 인한 제약을 고려하여 크기를 빽빽하게 잡습니다:"
|
|
|
|
#: src/enums/sizes.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::mem::{align_of, size_of};\n"
|
|
"\n"
|
|
"macro_rules! dbg_size {\n"
|
|
" ($t:ty) => {\n"
|
|
" println!(\"{}: size {} bytes, align: {} bytes\",\n"
|
|
" stringify!($t), size_of::<$t>(), align_of::<$t>());\n"
|
|
" };\n"
|
|
"}\n"
|
|
"\n"
|
|
"enum Foo {\n"
|
|
" A,\n"
|
|
" B,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" dbg_size!(Foo);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"use std::mem::{align_of, size_of};\n"
|
|
"\n"
|
|
"macro_rules! dbg_size {\n"
|
|
" ($t:ty) => {\n"
|
|
" println!(\"{}: size {} bytes, align: {} bytes\",\n"
|
|
" stringify!($t), size_of::<$t>(), align_of::<$t>());\n"
|
|
" };\n"
|
|
"}\n"
|
|
"\n"
|
|
"enum Foo {\n"
|
|
" A,\n"
|
|
" B,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" dbg_size!(Foo);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/enums/sizes.md:25
|
|
msgid "* See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout.html)."
|
|
msgstr "* 자세한 사항은 [공식문서](https://doc.rust-lang.org/reference/type-layout.html)를 확인하세요."
|
|
|
|
#: src/enums/sizes.md:31
|
|
msgid ""
|
|
" * Internally Rust is using a field (discriminant) to keep track of the enum variant.\n"
|
|
"\n"
|
|
" * You can control the discriminant if needed (e.g., for compatibility with C):\n"
|
|
" \n"
|
|
" ```rust,editable\n"
|
|
" #[repr(u32)]\n"
|
|
" enum Bar {\n"
|
|
" A, // 0\n"
|
|
" B = 10000,\n"
|
|
" C, // 10001\n"
|
|
" }\n"
|
|
" \n"
|
|
" fn main() {\n"
|
|
" println!(\"A: {}\", Bar::A as u32);\n"
|
|
" println!(\"B: {}\", Bar::B as u32);\n"
|
|
" println!(\"C: {}\", Bar::C as u32);\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
" Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2\n"
|
|
" bytes.\n"
|
|
"\n"
|
|
"\n"
|
|
" * Try out other types such as\n"
|
|
" \n"
|
|
" * `dbg_size!(bool)`: size 1 bytes, align: 1 bytes,\n"
|
|
" * `dbg_size!(Option<bool>)`: size 1 bytes, align: 1 bytes (niche optimization, see below),\n"
|
|
" * `dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine),\n"
|
|
" * `dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer optimization, see below).\n"
|
|
"\n"
|
|
" * Niche optimization: Rust will merge use unused bit patterns for the enum\n"
|
|
" discriminant.\n"
|
|
"\n"
|
|
" * Null pointer optimization: For [some\n"
|
|
" types](https://doc.rust-lang.org/std/option/#representation), Rust guarantees\n"
|
|
" that `size_of::<T>()` equals `size_of::<Option<T>>()`.\n"
|
|
"\n"
|
|
" Example code if you want to show how the bitwise representation *may* look like in practice.\n"
|
|
" It's important to note that the compiler provides no guarantees regarding this representation, therefore this is totally unsafe.\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" use std::mem::transmute;\n"
|
|
"\n"
|
|
" macro_rules! dbg_bits {\n"
|
|
" ($e:expr, $bit_type:ty) => {\n"
|
|
" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, $bit_type>($e));\n"
|
|
" };\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n"
|
|
" // representation of types.\n"
|
|
" unsafe {\n"
|
|
" println!(\"Bitwise representation of bool\");\n"
|
|
" dbg_bits!(false, u8);\n"
|
|
" dbg_bits!(true, u8);\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of Option<bool>\");\n"
|
|
" dbg_bits!(None::<bool>, u8);\n"
|
|
" dbg_bits!(Some(false), u8);\n"
|
|
" dbg_bits!(Some(true), u8);\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of Option<Option<bool>>\");\n"
|
|
" dbg_bits!(Some(Some(false)), u8);\n"
|
|
" dbg_bits!(Some(Some(true)), u8);\n"
|
|
" dbg_bits!(Some(None::<bool>), u8);\n"
|
|
" dbg_bits!(None::<Option<bool>>, u8);\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of Option<&i32>\");\n"
|
|
" dbg_bits!(None::<&i32>, usize);\n"
|
|
" dbg_bits!(Some(&0i32), usize);\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
" More complex example if you want to discuss what happens when we chain more than 256 `Option`s together.\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" #![recursion_limit = \"1000\"]\n"
|
|
"\n"
|
|
" use std::mem::transmute;\n"
|
|
" \n"
|
|
" macro_rules! dbg_bits {\n"
|
|
" ($e:expr, $bit_type:ty) => {\n"
|
|
" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, $bit_type>($e));\n"
|
|
" };\n"
|
|
" }\n"
|
|
"\n"
|
|
" // Macro to wrap a value in 2^n Some() where n is the number of \"@\" signs.\n"
|
|
" // Increasing the recursion limit is required to evaluate this macro.\n"
|
|
" macro_rules! many_options {\n"
|
|
" ($value:expr) => { Some($value) };\n"
|
|
" ($value:expr, @) => {\n"
|
|
" Some(Some($value))\n"
|
|
" };\n"
|
|
" ($value:expr, @ $($more:tt)+) => {\n"
|
|
" many_options!(many_options!($value, $($more)+), $($more)+)\n"
|
|
" };\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn main() {\n"
|
|
" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n"
|
|
" // representation of types.\n"
|
|
" unsafe {\n"
|
|
" assert_eq!(many_options!(false), Some(false));\n"
|
|
" assert_eq!(many_options!(false, @), Some(Some(false)));\n"
|
|
" assert_eq!(many_options!(false, @@), Some(Some(Some(Some(false)))));\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of a chain of 128 Option's.\");\n"
|
|
" dbg_bits!(many_options!(false, @@@@@@@), u8);\n"
|
|
" dbg_bits!(many_options!(true, @@@@@@@), u8);\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of a chain of 256 Option's.\");\n"
|
|
" dbg_bits!(many_options!(false, @@@@@@@@), u16);\n"
|
|
" dbg_bits!(many_options!(true, @@@@@@@@), u16);\n"
|
|
"\n"
|
|
" println!(\"Bitwise representation of a chain of 257 Option's.\");\n"
|
|
" dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);\n"
|
|
" dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);\n"
|
|
" dbg_bits!(many_options!(None::<bool>, @@@@@@@@), u16);\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```"
|
|
msgstr ""
|
|
"* 러스트는 열거형 variant를 구분하기 위해 내부적으로 식별자(discriminant) 필드를 사용합니다.\n"
|
|
"\n"
|
|
"* C와의 연동과 같은 이유로 식별자를 직접 지정할 수도 있습니다:\n"
|
|
" \n"
|
|
" ```rust,editable\n"
|
|
" #[repr(u32)]\n"
|
|
" enum Bar {\n"
|
|
" A, // 0\n"
|
|
" B = 10000,\n"
|
|
" C, // 10001\n"
|
|
" }\n"
|
|
" \n"
|
|
" fn main() {\n"
|
|
" println!(\"A: {}\", Bar::A as u32);\n"
|
|
" println!(\"B: {}\", Bar::B as u32);\n"
|
|
" println!(\"C: {}\", Bar::C as u32);\n"
|
|
" }\n"
|
|
" ```\n"
|
|
"\n"
|
|
" `repr` 속성이 없다면 10001이 2 바이트로 표현가능하기 때문에 식별자의 타입 크기는 2 바이트가 됩니다.\n"
|
|
"\n"
|
|
"* 다른 타입들도 확인해보세요.\n"
|
|
"\n"
|
|
" * `dbg_size!(bool)`: size 1 bytes, align: 1 bytes\n"
|
|
" * `dbg_size!(Option<bool>)`: size 1 bytes, align: 1 bytes (니치 최적화, 아래 설명 참조)\n"
|
|
" * `dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (64비트 머신인 경우)\n"
|
|
" * `dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (널포인터 최적화, 아래 설명 참조)\n"
|
|
"\n"
|
|
"* 니치 최적화: 러스트는 열거형 식별자를 사용되지 않은 비트 패턴과 병합합니다."
|
|
|
|
#: src/methods.md:3
|
|
msgid ""
|
|
"Rust allows you to associate functions with your new types. You do this with an\n"
|
|
"`impl` block:"
|
|
msgstr "러스트에서 선언된 타입에 대해 `impl`블록에 함수를 선언하여 메서드를 연결 할 수 있습니다:"
|
|
|
|
#: src/methods.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Person {\n"
|
|
" name: String,\n"
|
|
" age: u8,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Person {\n"
|
|
" fn say_hello(&self) {\n"
|
|
" println!(\"Hello, my name is {}\", self.name);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let peter = Person {\n"
|
|
" name: String::from(\"Peter\"),\n"
|
|
" age: 27,\n"
|
|
" };\n"
|
|
" peter.say_hello();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/methods.md:31
|
|
msgid ""
|
|
"* It can be helpful to introduce methods by comparing them to functions.\n"
|
|
" * Methods are called on an instance of a type (such as a struct or enum), the first parameter represents the instance as `self`.\n"
|
|
" * Developers may choose to use methods to take advantage of method receiver syntax and to help keep them more organized. By using methods we can keep all the implementation code in one predictable place.\n"
|
|
"* Point out the use of the keyword `self`, a method receiver. \n"
|
|
" * Show that it is an abbreviated term for `self:&Self` and perhaps show how the struct name could also be used. \n"
|
|
" * Explain that `Self` is a type alias for the type the `impl` block is in and can be used elsewhere in the block.\n"
|
|
" * Note how `self` is used like other structs and dot notation can be used to refer to individual fields.\n"
|
|
" * This might be a good time to demonstrate how the `&self` differs from `self` by modifying the code and trying to run say_hello twice. \n"
|
|
"* We describe the distinction between method receivers next.\n"
|
|
" "
|
|
msgstr ""
|
|
"* 메서드를 함수와 비교하여 소개하는 것도 도움이 될 수 있습니다.\n"
|
|
" * 메서드는 구조체나 열거형과 같은 타입의 인스턴스에서 호출 되며, 첫번째 매개변수(파라메터)는 인스턴스를 `self`로 표기합니다.\n"
|
|
" * 메서드를 이용하면 receiver 문법을 사용할 수 있고 코드를 좀더 체계적으로 정리할 수 있습니다. 메서드들이 예측 가능한 위치에 모여 있으니 찾기 쉽습니다.\n"
|
|
"* 메서드 receiver인 `self` 키워드 사용을 언급해 주시기 바랍니다.\n"
|
|
" * 예제의 경우 `self: &Self`의 줄인 버전임을 알려주고, 구조체의 이름을 직접 사용하면 어떻게 되는지 보여주는 것도 좋습니다.\n"
|
|
" * `impl` 블록 내부에서는 `Self`가 해당 타입의 이름 대용으로 사용될 수 있음을 알려주세요.\n"
|
|
" * 구조체의 필드를 접근할 때 점 표기를 사용하듯이 `self`에 점 표기를 사용하여 개별 필드들을 접근할 수 있습니다.\n"
|
|
" * `say_hello` 함수가 두 번 호출되도록 코드를 수정하여 `&self`와 `self`가 어떻게 다른지 보여주는 것도 좋습니다.\n"
|
|
"* 다음 슬라이드에서 receiver의 구분을 설명합니다.\n"
|
|
" "
|
|
|
|
#: src/methods/receiver.md:1
|
|
msgid "# Method Receiver"
|
|
msgstr "# 메서드 리시버(Receiver)"
|
|
|
|
#: src/methods/receiver.md:3
|
|
msgid ""
|
|
"The `&self` above indicates that the method borrows the object immutably. There\n"
|
|
"are other possible receivers for a method:"
|
|
msgstr "`&self`는 메서드가 객체를 불변하게 빌려옴을 나타냅니다. 메서드의 리시버는 다음의 형태들이 가능합니다:"
|
|
|
|
#: src/methods/receiver.md:6
|
|
msgid ""
|
|
"* `&self`: borrows the object from the caller using a shared and immutable\n"
|
|
" reference. The object can be used again afterwards.\n"
|
|
"* `&mut self`: borrows the object from the caller using a unique and mutable\n"
|
|
" reference. The object can be used again afterwards.\n"
|
|
"* `self`: takes ownership of the object and moves it away from the caller. The\n"
|
|
" method becomes the owner of the object. The object will be dropped (deallocated)\n"
|
|
" when the method returns, unless its ownership is explicitly\n"
|
|
" transmitted.\n"
|
|
"* `mut self`: same as above, but while the method owns the object, it can\n"
|
|
" mutate it too. Complete ownership does not automatically mean mutability.\n"
|
|
"* No receiver: this becomes a static method on the struct. Typically used to\n"
|
|
" create constructors which are called `new` by convention."
|
|
msgstr ""
|
|
"* `&self`: 호출자로부터 공유가능한 불변 참조 방식으로 객체를 빌려옴을 나타냅니다. 객체는 메소드 호출 뒤에도 사용될 수 있습니다.\n"
|
|
"* `&mut self`: 호출자로부터 유일한 가변 참조 방식으로 객체를 빌려옴을 나타냅니다. 객체는 메소드 호출 뒤에도 사용될 수 있습니다.\n"
|
|
"* `self`: 호출자로부터 객체의 소유권을 가져오고 객체는 호출자로부터 메소드로 이동됩니다. 메소드가 객체를 소유하게 되며 따라서 명시적으로 소유권을 다른 곳으로 전달하지 않는다면 메서드 종료와 함께 객체는 drop(해제)됩니다.\n"
|
|
"* `mut self`: 위와 동일하지만 메서드가 객체의 소유권을 가지면서 동시에 객체를 수정할 수도 있습니다. 소유권을 가지는 것이 수정할 수 있음을 의미하는 것은 아닙니다.\n"
|
|
"* 리시버 없음: 구조체의 정적 메서드가 됩니다. 주로 생성자를 만들때 사용하게 되며, 생성자는 흔히 `new`라고 이름붙입니다."
|
|
|
|
#: src/methods/receiver.md:19
|
|
msgid ""
|
|
"Beyond variants on `self`, there are also\n"
|
|
"[special wrapper types](https://doc.rust-lang.org/reference/special-types-and-traits.html)\n"
|
|
"allowed to be receiver types, such as `Box<Self>`."
|
|
msgstr "`self`를 사용하는 이같은 변형들 외에도 `Box<Self>`와 같이 리시버 타입으로 허용되는 [특별한 래퍼 타입](https://doc.rust-lang.org/reference/special-types-and-traits.html)이 있습니다."
|
|
|
|
#: src/methods/receiver.md:25
|
|
msgid ""
|
|
"Consider emphasizing \"shared and immutable\" and \"unique and mutable\". These constraints always come\n"
|
|
"together in Rust due to borrow checker rules, and `self` is no exception. It isn't possible to\n"
|
|
"reference a struct from multiple locations and call a mutating (`&mut self`) method on it."
|
|
msgstr "\"공유가능한 불변\"과 \"유일한 가변\" 부분은 강조할 만합니다. 이러한 제약은 러스트의 빌림 검사기(borrow checker) 규칙으로 늘 붙어다닙니다. `self`도 예외는 아닙니다. 여러 위치에서 구조체를 참조하면서 객체를 수정하는(`&mut self`를 리시버로 하는) 메서드를 호출하는 것은 불가능합니다."
|
|
|
|
#: src/methods/example.md:1 src/concurrency/shared_state/example.md:1
|
|
msgid "# Example"
|
|
msgstr "# 예제"
|
|
|
|
#: src/methods/example.md:3
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Race {\n"
|
|
" name: String,\n"
|
|
" laps: Vec<i32>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Race {\n"
|
|
" fn new(name: &str) -> Race { // No receiver, a static method\n"
|
|
" Race { name: String::from(name), laps: Vec::new() }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn add_lap(&mut self, lap: i32) { // Exclusive borrowed read-write access to self\n"
|
|
" self.laps.push(lap);\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn print_laps(&self) { // Shared and read-only borrowed access to self\n"
|
|
" println!(\"Recorded {} laps for {}:\", self.laps.len(), self.name);\n"
|
|
" for (idx, lap) in self.laps.iter().enumerate() {\n"
|
|
" println!(\"Lap {idx}: {lap} sec\");\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn finish(self) { // Exclusive ownership of self\n"
|
|
" let total = self.laps.iter().sum::<i32>();\n"
|
|
" println!(\"Race {} is finished, total lap time: {}\", self.name, total);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut race = Race::new(\"Monaco Grand Prix\");\n"
|
|
" race.add_lap(70);\n"
|
|
" race.add_lap(68);\n"
|
|
" race.print_laps();\n"
|
|
" race.add_lap(71);\n"
|
|
" race.print_laps();\n"
|
|
" race.finish();\n"
|
|
" // race.add_lap(42);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/methods/example.md:47
|
|
msgid ""
|
|
"* All four methods here use a different method receiver.\n"
|
|
" * You can point out how that changes what the function can do with the variable values and if/how it can be used again in `main`.\n"
|
|
" * You can showcase the error that appears when trying to call `finish` twice.\n"
|
|
"* Note that although the method receivers are different, the non-static functions are called the same way in the main body. Rust enables automatic referencing and dereferencing when calling methods. Rust automatically adds in the `&`, `*`, `muts` so that that object matches the method signature.\n"
|
|
"* You might point out that `print_laps` is using a vector that is iterated over. We describe vectors in more detail in the afternoon. "
|
|
msgstr ""
|
|
"* 4가지 유형의 메서드 receiver에 대해 설명합니다.\n"
|
|
" * receiver 유형에 따라 함수가 할 수 있는 일이 달라지고, 또 메소드를 호출한 뒤 `main`에서 해당 객체를 사용할 수 있는지 여부도 달라진다는 점을 강조하세요.\n"
|
|
" * `finish`를 두번 호출하여 오류가 발생하는 것을 보일 수 있습니다.\n"
|
|
"* 비록 메서드 receiver는 다르지만 main 함수에서 비 정적 함수를 부르는 방법은 같습니다. 러스트는 메서드를 호출할 때 자동으로 참조/역참조(따라가기)를 수행합니다. 러스트는 객체와 매서드 시그니처가 서로 매치되도록 객체에 `&`, `*`, `muts`를 자동으로 붙여줍니다.\n"
|
|
"* `print_laps`함수에서 벡터를 어떤 식으로 사용하고 있는지 언급하는 것도 좋습니다. 벡터는 오후 강의에서 더 자세히 설명할 것입니다. "
|
|
|
|
#: src/pattern-matching.md:1
|
|
msgid "# Pattern Matching"
|
|
msgstr "# 패턴 매칭"
|
|
|
|
#: src/pattern-matching.md:3
|
|
msgid ""
|
|
"The `match` keyword let you match a value against one or more _patterns_. The\n"
|
|
"comparisons are done from top to bottom and the first match wins."
|
|
msgstr "`match`키워드는 값을 여러 형태의 패턴과 매치시킬 수 있습니다. 맨 위 패턴부터 하나씩 매치되는지 검사하며, 처음으로 매치되는 패턴이 선택됩니다."
|
|
|
|
#: src/pattern-matching.md:6
|
|
msgid "The patterns can be simple values, similarly to `switch` in C and C++:"
|
|
msgstr "C/C++의 `switch`와 비슷하게 값을 패턴으로 사용할 수도 있습니다:"
|
|
|
|
#: src/pattern-matching.md:8
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let input = 'x';\n"
|
|
"\n"
|
|
" match input {\n"
|
|
" 'q' => println!(\"Quitting\"),\n"
|
|
" 'a' | 's' | 'w' | 'd' => println!(\"Moving around\"),\n"
|
|
" '0'..='9' => println!(\"Number input\"),\n"
|
|
" _ => println!(\"Something else\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let input = 'x';\n"
|
|
"\n"
|
|
" match input {\n"
|
|
" 'q' => println!(\"Quitting\"),\n"
|
|
" 'a' | 's' | 'w' | 'd' => println!(\"Moving around\"),\n"
|
|
" '0'..='9' => println!(\"Number input\"),\n"
|
|
" _ => println!(\"Something else\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/pattern-matching.md:21
|
|
msgid "The `_` pattern is a wildcard pattern which matches any value."
|
|
msgstr "`_`패턴은 어떤 값과도 매칭되는 와일드카드입니다."
|
|
|
|
#: src/pattern-matching.md:26
|
|
msgid ""
|
|
"* You might point out how some specific characters are being used when in a pattern\n"
|
|
" * `|` as an `or`\n"
|
|
" * `..` can expand as much as it needs to be\n"
|
|
" * `1..=5` represents an inclusive range\n"
|
|
" * `_` is a wild card\n"
|
|
"* It can be useful to show how binding works, by for instance replacing a wildcard character with a variable, or removing the quotes around `q`.\n"
|
|
"* You can demonstrate matching on a reference.\n"
|
|
"* This might be a good time to bring up the concept of irrefutable patterns, as the term can show up in error messages.\n"
|
|
" "
|
|
msgstr ""
|
|
"* 패턴에서 사용되는 특수 문자들을 알려주세요.\n"
|
|
" * `|`: or 기호입니다.\n"
|
|
" * `..`: 필요한 만큼 확장합니다.\n"
|
|
" * `1..=5`: 끝 값(여기서는 5)을 포함하는 범위를 나타냅니다.\n"
|
|
" * `_`: 와일드카드입니다.\n"
|
|
"* 와일드카드 문자를 변수로 바꾸거나 `q`의 따옴표를 제거하는 식으로 수정하면서 바인딩이 어떻게 작동하는지 보여주는 것도 유용할 수 있습니다.\n"
|
|
"* 참조를 매칭하는 것도 시연할 수 있습니다.\n"
|
|
"* 에러 메시지에 \"반박 불가능 패턴(irrefutable pattern)\"이란 용어가 등장하기도 합니다. 지금 그 의미를 소개하는 것도 좋을 것 같습니다.\n"
|
|
" "
|
|
|
|
#: src/pattern-matching/destructuring-enums.md:1
|
|
msgid "# Destructuring Enums"
|
|
msgstr "# 열거형 분해(역구조화)"
|
|
|
|
#: src/pattern-matching/destructuring-enums.md:3
|
|
msgid ""
|
|
"Patterns can also be used to bind variables to parts of your values. This is how\n"
|
|
"you inspect the structure of your types. Let us start with a simple `enum` type:"
|
|
msgstr "구조체나 열거형 값의 일부를 패턴 매치를 통해 변수에 바인딩할 수 있습니다. 간단한 `enum` 타입을 먼저 살펴보겠습니다:"
|
|
|
|
#: src/pattern-matching/destructuring-enums.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"enum Result {\n"
|
|
" Ok(i32),\n"
|
|
" Err(String),\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn divide_in_two(n: i32) -> Result {\n"
|
|
" if n % 2 == 0 {\n"
|
|
" Result::Ok(n / 2)\n"
|
|
" } else {\n"
|
|
" Result::Err(format!(\"cannot divide {n} into two equal parts\"))\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let n = 100;\n"
|
|
" match divide_in_two(n) {\n"
|
|
" Result::Ok(half) => println!(\"{n} divided in two is {half}\"),\n"
|
|
" Result::Err(msg) => println!(\"sorry, an error happened: {msg}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"enum Result {\n"
|
|
" Ok(i32),\n"
|
|
" Err(String),\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn divide_in_two(n: i32) -> Result {\n"
|
|
" if n % 2 == 0 {\n"
|
|
" Result::Ok(n / 2)\n"
|
|
" } else {\n"
|
|
" Result::Err(format!(\"cannot divide {n} into two equal parts\"))\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let n = 100;\n"
|
|
" match divide_in_two(n) {\n"
|
|
" Result::Ok(half) => println!(\"{n} divided in two is {half}\"),\n"
|
|
" Result::Err(msg) => println!(\"sorry, an error happened: {msg}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/pattern-matching/destructuring-enums.md:29
|
|
msgid ""
|
|
"Here we have used the arms to _destructure_ the `Result` value. In the first\n"
|
|
"arm, `half` is bound to the value inside the `Ok` variant. In the second arm,\n"
|
|
"`msg` is bound to the error message."
|
|
msgstr "`match`구문에서 `divide_in_two`함수에서 반환되는 `Result` 값을 두 개의 팔(혹은 가지)로 _분해(destructure)_ 하였습니다. 첫번째 팔에서 `half`는 `Ok` variant에 담긴 값으로 바인딩됩니다. 두번째 팔에서 `msg`는 오류 메시지 문자열에 바인딩됩니다."
|
|
|
|
#: src/pattern-matching/destructuring-enums.md:36
|
|
msgid ""
|
|
"* The `if`/`else` expression is returning an enum that is later unpacked with a `match`.\n"
|
|
"* You can try adding a third variant to the enum definition and displaying the errors when running the code. Point out the places where your code is now inexhaustive and how the compiler tries to give you hints."
|
|
msgstr ""
|
|
"* `if`/`else` 표현식은 열거형을 반환하고, 이 값은 나중에 `match`로 분해됩니다.\n"
|
|
"* 열거형에 세번째 variant를 추가하고 코드를 실행하여 오류를 표시해보세요. 코드 어느 부분에 누락이 있는지, 그리고 컴파일러가 어떤 식으로 힌트를 주는지 같이 살펴보세요."
|
|
|
|
#: src/pattern-matching/destructuring-structs.md:1
|
|
msgid "# Destructuring Structs"
|
|
msgstr "# 구조체 분해(역구조화)"
|
|
|
|
#: src/pattern-matching/destructuring-structs.md:3
|
|
msgid "You can also destructure `structs`:"
|
|
msgstr "`struct` 구조체 역시 분해할 수 있습니다:"
|
|
|
|
#: src/pattern-matching/destructuring-structs.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Foo {\n"
|
|
" x: (u32, u32),\n"
|
|
" y: u32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let foo = Foo { x: (1, 2), y: 3 };\n"
|
|
" match foo {\n"
|
|
" Foo { x: (1, b), y } => println!(\"x.0 = 1, b = {b}, y = {y}\"),\n"
|
|
" Foo { y: 2, x: i } => println!(\"y = 2, x = {i:?}\"),\n"
|
|
" Foo { y, .. } => println!(\"y = {y}, other fields were ignored\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"struct Foo {\n"
|
|
" x: (u32, u32),\n"
|
|
" y: u32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let foo = Foo { x: (1, 2), y: 3 };\n"
|
|
" match foo {\n"
|
|
" Foo { x: (1, b), y } => println!(\"x.0 = 1, b = {b}, y = {y}\"),\n"
|
|
" Foo { y: 2, x: i } => println!(\"y = 2, x = {i:?}\"),\n"
|
|
" Foo { y, .. } => println!(\"y = {y}, other fields were ignored\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/pattern-matching/destructuring-structs.md:23
|
|
msgid ""
|
|
"* Change the literal values in `foo` to match with the other patterns.\n"
|
|
"* Add a new field to `Foo` and make changes to the pattern as needed."
|
|
msgstr ""
|
|
"* `foo` 인스턴스 생성에 사용된 리터럴 값을 변경해서 다른 패턴에 매치되도록 해보세요.\n"
|
|
"* `Foo` 구조체에 새 필드를 추가하고 패턴도 바꿔보세요."
|
|
|
|
#: src/pattern-matching/destructuring-arrays.md:1
|
|
msgid "# Destructuring Arrays"
|
|
msgstr "# 배열 분해(역구조화)"
|
|
|
|
#: src/pattern-matching/destructuring-arrays.md:3
|
|
msgid "You can destructure arrays, tuples, and slices by matching on their elements:"
|
|
msgstr "배열이나 튜플, 슬라이스도 그 요소들에 대해 패턴 매칭으로 분해할 수 있습니다:"
|
|
|
|
#: src/pattern-matching/destructuring-arrays.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let triple = [0, -2, 3];\n"
|
|
" println!(\"Tell me about {triple:?}\");\n"
|
|
" match triple {\n"
|
|
" [0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n"
|
|
" [1, ..] => println!(\"First is 1 and the rest were ignored\"),\n"
|
|
" _ => println!(\"All elements were ignored\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let triple = [0, -2, 3];\n"
|
|
" println!(\"Tell me about {triple:?}\");\n"
|
|
" match triple {\n"
|
|
" [0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n"
|
|
" [1, ..] => println!(\"First is 1 and the rest were ignored\"),\n"
|
|
" _ => println!(\"All elements were ignored\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/pattern-matching/destructuring-arrays.md:21
|
|
msgid ""
|
|
"* Destructuring of slices of unknown length also works with patterns of fixed length.\n"
|
|
"\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" fn main() {\n"
|
|
" inspect(&[0, -2, 3]);\n"
|
|
" inspect(&[0, -2, 3, 4]);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[rustfmt::skip]\n"
|
|
" fn inspect(slice: &[i32]) {\n"
|
|
" println!(\"Tell me about {slice:?}\");\n"
|
|
" match slice {\n"
|
|
" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n"
|
|
" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n"
|
|
" _ => println!(\"All elements were ignored\"),\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```\n"
|
|
" \n"
|
|
"* Create a new pattern using `_` to represent an element. \n"
|
|
"* Add more values to the array.\n"
|
|
"* Point out that how `..` will expand to account for different number of elements.\n"
|
|
"* Show matching against the tail with patterns `[.., b]` and `[a@..,b]`"
|
|
msgstr ""
|
|
"* 길이를 알 수 없는 슬라이스에 대해서도 고정 길이 패턴으로 분해할 수 있습니다.\n"
|
|
"\n"
|
|
"\n"
|
|
" ```rust,editable\n"
|
|
" fn main() {\n"
|
|
" inspect(&[0, -2, 3]);\n"
|
|
" inspect(&[0, -2, 3, 4]);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[rustfmt::skip]\n"
|
|
" fn inspect(slice: &[i32]) {\n"
|
|
" println!(\"Tell me about {slice:?}\");\n"
|
|
" match slice {\n"
|
|
" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n"
|
|
" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n"
|
|
" _ => println!(\"All elements were ignored\"),\n"
|
|
" }\n"
|
|
" }\n"
|
|
" ```\n"
|
|
" \n"
|
|
"* `_`를 사용하여 요소를 매칭하는 패턴을 추가해보세요.\n"
|
|
"* 배열에 값을 더 추가해보세요.\n"
|
|
"* `..`가 요소 개수에 상관없이 매치될 수 있음을 알려주세요.\n"
|
|
"* `[.., b]`나 `[a@.., b]`와 같은 패턴으로 꼬리 부분을 매칭하는 것을 보여주세요."
|
|
|
|
#: src/pattern-matching/match-guards.md:1
|
|
msgid "# Match Guards"
|
|
msgstr "# 매치 가드"
|
|
|
|
#: src/pattern-matching/match-guards.md:3
|
|
msgid ""
|
|
"When matching, you can add a _guard_ to a pattern. This is an arbitrary Boolean\n"
|
|
"expression which will be executed if the pattern matches:"
|
|
msgstr "패턴 뒤에 가드(guard, 조건식)를 덧붙일 수 있습니다. 가드는 패턴이 매치되면 추가로 따져보는 불리언 표현식입니다:"
|
|
|
|
#: src/pattern-matching/match-guards.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let pair = (2, -2);\n"
|
|
" println!(\"Tell me about {pair:?}\");\n"
|
|
" match pair {\n"
|
|
" (x, y) if x == y => println!(\"These are twins\"),\n"
|
|
" (x, y) if x + y == 0 => println!(\"Antimatter, kaboom!\"),\n"
|
|
" (x, _) if x % 2 == 1 => println!(\"The first one is odd\"),\n"
|
|
" _ => println!(\"No correlation...\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"#[rustfmt::skip]\n"
|
|
"fn main() {\n"
|
|
" let pair = (2, -2);\n"
|
|
" println!(\"Tell me about {pair:?}\");\n"
|
|
" match pair {\n"
|
|
" (x, y) if x == y => println!(\"These are twins\"),\n"
|
|
" (x, y) if x + y == 0 => println!(\"Antimatter, kaboom!\"),\n"
|
|
" (x, _) if x % 2 == 1 => println!(\"The first one is odd\"),\n"
|
|
" _ => println!(\"No correlation...\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/pattern-matching/match-guards.md:23
|
|
msgid ""
|
|
"* Match guards as a separate syntax feature are important and necessary when we wish to concisely express more complex ideas than patterns alone would allow.\n"
|
|
"* They are not the same as separate `if` expression inside of the match arm. An `if` expression inside of the branch block (after `=>`) happens after the match arm is selected. Failing the `if` condition inside of that block won't result in other arms\n"
|
|
"of the original `match` expression being considered. \n"
|
|
"* You can use the variables defined in the pattern in your if expression.\n"
|
|
"* The condition defined in the guard applies to every expression in a pattern with an `|`."
|
|
msgstr ""
|
|
"* 매치 가드는 별도의 문법 요소로서 패턴 자체만으로 표현하기 어려운 복잡한 경우를 간결하게 표현하고자 할 때 유용합니다.\n"
|
|
"* 매치의 각 팔(혹은 가지) 안에 따로 `if`를 사용한 것과 다릅니다. 매치 가지의 `=>` 뒤에 사용된 `if` 표현식은 해당 가지가 선택된 다음에 실행됩니다. 따라서 여기서 `if` 조건이 실패하더라도 원래 `match`의 다른 가지는 고려되지 않습니다.\n"
|
|
"* 패턴에 정의된 변수를 가드의 표현식에서 사용할 수 있습니다.\n"
|
|
"* 가드에 정의된 조건은 `|` 를 포함하는 패턴의 모든 표현식에 적용됩니다."
|
|
|
|
#: src/exercises/day-2/morning.md:1
|
|
msgid "# Day 2: Morning Exercises"
|
|
msgstr "# 2일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-2/morning.md:3
|
|
msgid "We will look at implementing methods in two contexts:"
|
|
msgstr "이번 연습문제들은 두가지 맥락에서 메서드 구현방법을 다룹니다:"
|
|
|
|
#: src/exercises/day-2/morning.md:5
|
|
msgid ""
|
|
"* Simple struct which tracks health statistics.\n"
|
|
"\n"
|
|
"* Multiple structs and enums for a drawing library."
|
|
msgstr ""
|
|
"* 건강 상태 통계를 추적하는 프로그램의 간단한 구조체.\n"
|
|
"\n"
|
|
"* 드로잉 라이브러리를 위한 구조체 및 열거헝."
|
|
|
|
#: src/exercises/day-2/health-statistics.md:1
|
|
msgid "# Health Statistics"
|
|
msgstr "# 건강상태 모니터링 시스템"
|
|
|
|
#: src/exercises/day-2/health-statistics.md:3
|
|
msgid ""
|
|
"You're working on implementing a health-monitoring system. As part of that, you\n"
|
|
"need to keep track of users' health statistics."
|
|
msgstr "당신은 건강 상태를 모니터링하는 시스템을 구현하는 일을 하고 있습니다. 그 일환으로 당신은 사용자의 건강 상태 통계를 추적해야합니다."
|
|
|
|
#: src/exercises/day-2/health-statistics.md:6
|
|
msgid ""
|
|
"You'll start with some stubbed functions in an `impl` block as well as a `User`\n"
|
|
"struct definition. Your goal is to implement the stubbed out methods on the\n"
|
|
"`User` `struct` defined in the `impl` block."
|
|
msgstr "당신의 목표는 `User` 구조체의 `impl` 블록의 빈 함수를 구현하는 것입니다."
|
|
|
|
#: src/exercises/day-2/health-statistics.md:10
|
|
msgid ""
|
|
"Copy the code below to <https://play.rust-lang.org/> and fill in the missing\n"
|
|
"methods:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사해서 빠진 메서드를 구현하면 됩니다:"
|
|
|
|
#: src/exercises/day-2/health-statistics.md:13
|
|
msgid ""
|
|
"```rust,should_panic\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"struct User {\n"
|
|
" name: String,\n"
|
|
" age: u32,\n"
|
|
" weight: f32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl User {\n"
|
|
" pub fn new(name: String, age: u32, weight: f32) -> Self {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn name(&self) -> &str {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn age(&self) -> u32 {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn weight(&self) -> f32 {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn set_age(&mut self, new_age: u32) {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn set_weight(&mut self, new_weight: f32) {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n"
|
|
" println!(\"I'm {} and my age is {}\", bob.name(), bob.age());\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_weight() {\n"
|
|
" let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n"
|
|
" assert_eq!(bob.weight(), 155.2);\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_set_age() {\n"
|
|
" let mut bob = User::new(String::from(\"Bob\"), 32, 155.2);\n"
|
|
" assert_eq!(bob.age(), 32);\n"
|
|
" bob.set_age(33);\n"
|
|
" assert_eq!(bob.age(), 33);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/points-polygons.md:1
|
|
msgid "# Polygon Struct"
|
|
msgstr "# Polygon 구조체"
|
|
|
|
#: src/exercises/day-2/points-polygons.md:3
|
|
msgid ""
|
|
"We will create a `Polygon` struct which contain some points. Copy the code below\n"
|
|
"to <https://play.rust-lang.org/> and fill in the missing methods to make the\n"
|
|
"tests pass:"
|
|
msgstr "우리는 몇개의 꼭지점을 가진 다각형을 표현하는 `Polygon` 구조체를 만들 것입니다. 아래 코드를 <https://play.rust-lang.org/>에 복사해서 테스트가 통과하도록 빠진 메서드를 구현하시면 됩니다:"
|
|
|
|
#: src/exercises/day-2/points-polygons.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"pub struct Point {\n"
|
|
" // add fields\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Point {\n"
|
|
" // add methods\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Polygon {\n"
|
|
" // add fields\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Polygon {\n"
|
|
" // add methods\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Circle {\n"
|
|
" // add fields\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Circle {\n"
|
|
" // add methods\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub enum Shape {\n"
|
|
" Polygon(Polygon),\n"
|
|
" Circle(Circle),\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[cfg(test)]\n"
|
|
"mod tests {\n"
|
|
" use super::*;\n"
|
|
"\n"
|
|
" fn round_two_digits(x: f64) -> f64 {\n"
|
|
" (x * 100.0).round() / 100.0\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_magnitude() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_dist() {\n"
|
|
" let p1 = Point::new(10, 10);\n"
|
|
" let p2 = Point::new(14, 13);\n"
|
|
" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_add() {\n"
|
|
" let p1 = Point::new(16, 16);\n"
|
|
" let p2 = p1 + Point::new(-4, 3);\n"
|
|
" assert_eq!(p2, Point::new(12, 19));\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_polygon_left_most_point() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" let p2 = Point::new(16, 16);\n"
|
|
"\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(p1);\n"
|
|
" poly.add_point(p2);\n"
|
|
" assert_eq!(poly.left_most_point(), Some(p1));\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_polygon_iter() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" let p2 = Point::new(16, 16);\n"
|
|
"\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(p1);\n"
|
|
" poly.add_point(p2);\n"
|
|
"\n"
|
|
" let points = poly.iter().cloned().collect::<Vec<_>>();\n"
|
|
" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_shape_perimeters() {\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(Point::new(12, 13));\n"
|
|
" poly.add_point(Point::new(17, 11));\n"
|
|
" poly.add_point(Point::new(16, 16));\n"
|
|
" let shapes = vec![\n"
|
|
" Shape::from(poly),\n"
|
|
" Shape::from(Circle::new(Point::new(10, 20), 5)),\n"
|
|
" ];\n"
|
|
" let perimeters = shapes\n"
|
|
" .iter()\n"
|
|
" .map(Shape::perimeter)\n"
|
|
" .map(round_two_digits)\n"
|
|
" .collect::<Vec<_>>();\n"
|
|
" assert_eq!(perimeters, vec![15.48, 31.42]);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[allow(dead_code)]\n"
|
|
"fn main() {}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/points-polygons.md:117
|
|
msgid ""
|
|
"Since the method signatures are missing from the problem statements, the key part\n"
|
|
"of the exercise is to specify those correctly. You don't have to modify the tests."
|
|
msgstr "누락된 메서드 시그니처를 올바르게 정의하는 것이 문제의 핵심 부분입니다. 테스트는 수정하면 안됩니다."
|
|
|
|
#: src/exercises/day-2/points-polygons.md:120
|
|
msgid "Other interesting parts of the exercise:"
|
|
msgstr "연습문제의 다른 흥미로운 부분:"
|
|
|
|
#: src/exercises/day-2/points-polygons.md:122
|
|
msgid ""
|
|
"* Derive a `Copy` trait for some structs, as in tests the methods sometimes don't borrow their arguments.\n"
|
|
"* Discover that `Add` trait must be implemented for two objects to be addable via \"+\". Note that we do not discuss generics until Day 3."
|
|
msgstr ""
|
|
"* 테스트 코드를 보면 어떤 메서드들은 인자를 borrow하는 대신 `Copy` 트레잇을 사용하기도 합니다. 구조체가 `Copy` 트레잇을 상속(derive)하도록 하면 됩니다.\n"
|
|
"* \"+\"를 사용하여 두 객체를 서로 더하려면 `Add` 트레잇을 구현해야 합니다. 이는 3일차에 다룰 내용입니다."
|
|
|
|
#: src/control-flow.md:1
|
|
msgid "# Control Flow"
|
|
msgstr "# 흐름 제어"
|
|
|
|
#: src/control-flow.md:3
|
|
msgid ""
|
|
"As we have seen, `if` is an expression in Rust. It is used to conditionally\n"
|
|
"evaluate one of two blocks, but the blocks can have a value which then becomes\n"
|
|
"the value of the `if` expression. Other control flow expressions work similarly\n"
|
|
"in Rust."
|
|
msgstr "앞에서 살펴본 바와 같이 러스트에서 `if`는 표현식입니다. 조건에 따라 두 블록 중 하나를 평가하며, 그 결과값이 `if` 표현식의 값이 됩니다. 다른 흐름제어 표현식도 유사하게 작동합니다."
|
|
|
|
#: src/control-flow/blocks.md:1
|
|
msgid "# Blocks"
|
|
msgstr "# 블록"
|
|
|
|
#: src/control-flow/blocks.md:3
|
|
msgid ""
|
|
"A block in Rust has a value and a type: the value is the last expression of the\n"
|
|
"block:"
|
|
msgstr "러스트에서 블록은 값과 타입을 갖습니다. 블록의 마지막 표현식이 블록의 값이 됩니다:"
|
|
|
|
#: src/control-flow/blocks.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let x = {\n"
|
|
" let y = 10;\n"
|
|
" println!(\"y: {y}\");\n"
|
|
" let z = {\n"
|
|
" let w = {\n"
|
|
" 3 + 4\n"
|
|
" };\n"
|
|
" println!(\"w: {w}\");\n"
|
|
" y * w\n"
|
|
" };\n"
|
|
" println!(\"z: {z}\");\n"
|
|
" z - y\n"
|
|
" };\n"
|
|
" println!(\"x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/blocks.md:25
|
|
msgid ""
|
|
"The same rule is used for functions: the value of the function body is the\n"
|
|
"return value:"
|
|
msgstr "함수에도 동일한 규칙이 적용됩니다. 함수 바디를 이루는 블록의 값이 반환값이 됩니다:"
|
|
|
|
#: src/control-flow/blocks.md:28
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn double(x: i32) -> i32 {\n"
|
|
" x + x\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"doubled: {}\", double(7));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn double(x: i32) -> i32 {\n"
|
|
" x + x\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"doubled: {}\", double(7));\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/control-flow/blocks.md:38
|
|
msgid "However if the last expression ends with `;`, then the resulting value and type is `()`."
|
|
msgstr "위의 `main` 함수는 마지막 표현식이 `;`로 끝나기 때문에 반환되는 값과 타입이 `()`입니다."
|
|
|
|
#: src/control-flow/blocks.md:43
|
|
msgid ""
|
|
"* The point of this slide is to show that blocks have a type and value in Rust. \n"
|
|
"* You can show how the value of the block changes by changing the last line in the block. For instance, adding/removing a semicolon or using a `return`.\n"
|
|
" "
|
|
msgstr ""
|
|
"* 러스트에서는 블록이 타입과 값을 가진다는 점이 이 슬라이드의 핵심입니다.\n"
|
|
"* 블록 마지막 줄을 수정하면서 블록의 값이 어떻게 바뀌는지 보여주세요. 예를 들어, 세미콜론을 넣거나 뺀다든지, 아니면 `return`을 사용해 보세요.\n"
|
|
" "
|
|
|
|
#: src/control-flow/if-expressions.md:1
|
|
msgid "# `if` expressions"
|
|
msgstr "# `if` 표현식"
|
|
|
|
#: src/control-flow/if-expressions.md:3
|
|
msgid "You use `if` very similarly to how you would in other languages:"
|
|
msgstr "`if`는 다른 언어와 매우 유사합니다:"
|
|
|
|
#: src/control-flow/if-expressions.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut x = 10;\n"
|
|
" if x % 2 == 0 {\n"
|
|
" x = x / 2;\n"
|
|
" } else {\n"
|
|
" x = 3 * x + 1;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/if-expressions.md:16
|
|
msgid "In addition, you can use it as an expression. This does the same as above:"
|
|
msgstr "게다가 `if`는 표현식으로 사용할 수도 있습니다. 아래 코드는 위와 동일합니다:"
|
|
|
|
#: src/control-flow/if-expressions.md:18
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut x = 10;\n"
|
|
" x = if x % 2 == 0 {\n"
|
|
" x / 2\n"
|
|
" } else {\n"
|
|
" 3 * x + 1\n"
|
|
" };\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/if-expressions.md:31
|
|
msgid "Because `if` is an expression and must have a particular type, both of its branch blocks must have the same type. Consider showing what happens if you add `;` after `x / 2` in the second example."
|
|
msgstr "`if`는 표현식이고 타입을 가져야 하므로 분기 블록은 모두 같은 타입을 가져야 합니다. 두번째 예시의 `x / 2` 뒤에 `;`를 추가하여 어떻게 되는지 확인해 보시기 바랍니다."
|
|
|
|
#: src/control-flow/if-let-expressions.md:1
|
|
msgid "# `if let` expressions"
|
|
msgstr "# `if let` 표현식"
|
|
|
|
#: src/control-flow/if-let-expressions.md:3
|
|
msgid "If you want to match a value against a pattern, you can use `if let`:"
|
|
msgstr "어떤 값이 패턴에 매치되는지 검사하려면 `if let` 표현식을 사용하면 됩니다:"
|
|
|
|
#: src/control-flow/if-let-expressions.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let arg = std::env::args().next();\n"
|
|
" if let Some(value) = arg {\n"
|
|
" println!(\"Program name: {value}\");\n"
|
|
" } else {\n"
|
|
" println!(\"Missing name?\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/if-let-expressions.md:16
|
|
#: src/control-flow/while-let-expressions.md:21
|
|
#: src/control-flow/match-expressions.md:22
|
|
msgid ""
|
|
"See [pattern matching](../pattern-matching.md) for more details on patterns in\n"
|
|
"Rust."
|
|
msgstr "패턴에 관한 설명은 [패턴 매칭](../pattern-matching.md)을 참조하세요."
|
|
|
|
#: src/control-flow/if-let-expressions.md:21
|
|
msgid ""
|
|
"* `if let` can be more concise than `match`, e.g., when only one case is interesting. In contrast, `match` requires all branches to be covered.\n"
|
|
" * For the similar use case consider demonstrating a newly stabilized [`let else`](https://github.com/rust-lang/rust/pull/93628) feature.\n"
|
|
"* A common usage is handling `Some` values when working with `Option`.\n"
|
|
"* Unlike `match`, `if let` does not support guard clauses for pattern matching."
|
|
msgstr ""
|
|
"* `if let`은 `match`보다 더 간결할 수 있습니다. 예를 들어 특정한 경우만 고려하고 싶더라도 `match`는 모든 분기를 포함해야 합니다.\n"
|
|
" * 비슷한 사용례로 새롭게 \"stable\" 릴리즈 된 [`let else`](https://github.com/rust-lang/rust/pull/93628) 기능을 시연하는 것도 고려해 보시기 바랍니다.\n"
|
|
"* 일반적으로 `Option`으로 작업할 때 `Some` 값을 처리하는 식으로 사용됩니다.\n"
|
|
"* `match`와 다르게 `if let` 패턴 매칭에서는 가드를 지원하지 않습니다."
|
|
|
|
#: src/control-flow/while-expressions.md:1
|
|
msgid "# `while` expressions"
|
|
msgstr "# `while` 표현식"
|
|
|
|
#: src/control-flow/while-expressions.md:3
|
|
msgid "The `while` keyword works very similar to other languages:"
|
|
msgstr "`while` 역시 다른 언어과 매우 비슷합니다:"
|
|
|
|
#: src/control-flow/while-expressions.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut x = 10;\n"
|
|
" while x != 1 {\n"
|
|
" x = if x % 2 == 0 {\n"
|
|
" x / 2\n"
|
|
" } else {\n"
|
|
" 3 * x + 1\n"
|
|
" };\n"
|
|
" }\n"
|
|
" println!(\"Final x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/while-let-expressions.md:1
|
|
msgid "# `while let` expressions"
|
|
msgstr "# `while let` 표현식"
|
|
|
|
#: src/control-flow/while-let-expressions.md:3
|
|
msgid ""
|
|
"Like with `if`, there is a `while let` variant which repeatedly tests a value\n"
|
|
"against a pattern:"
|
|
msgstr "`if`와 동일하게 `while let`구문 역시 패턴매칭에 사용 할 수 있습니다:"
|
|
|
|
#: src/control-flow/while-let-expressions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
" let mut iter = v.into_iter();\n"
|
|
"\n"
|
|
" while let Some(x) = iter.next() {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
" let mut iter = v.into_iter();\n"
|
|
"\n"
|
|
" while let Some(x) = iter.next() {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/control-flow/while-let-expressions.md:17
|
|
msgid ""
|
|
"Here the iterator returned by `v.iter()` will return a `Option<i32>` on every\n"
|
|
"call to `next()`. It returns `Some(x)` until it is done, after which it will\n"
|
|
"return `None`. The `while let` lets us keep iterating through all items."
|
|
msgstr "`v.into_iter()`가 반환한 반복자는 `next()`가 호출될 때마다 `Option<i32>`를 반환합니다. 반복자가 완료될 때까지는 `Some(x)`를 반환하고 마지막엔 `None`을 반환합니다. `while let`을 통해 반복자의 모든 아이템을 확인할 수 있습니다."
|
|
|
|
#: src/control-flow/while-let-expressions.md:27
|
|
msgid ""
|
|
"* Point out that the `while let` loop will keep going as long as the value matches the pattern.\n"
|
|
"* You could rewrite the `while let` loop as an infinite loop with an if statement that breaks when there is no value to unwrap for `iter.next()`. The `while let` provides syntactic sugar for the above scenario.\n"
|
|
" "
|
|
msgstr ""
|
|
"* `while let`은 값이 패턴에 매치되는 동안 계속됩니다.\n"
|
|
"* `while let` 루프 대신 무한 루프를 사용하고 `iter.next()`가 빈 값을 반환할 때 루프를 빠져나오도록 작성할수도 있습니다. `while let`은 그러한 경우를 위한 문법적 편의를 제공합니다.\n"
|
|
" "
|
|
|
|
#: src/control-flow/for-expressions.md:1
|
|
msgid "# `for` expressions"
|
|
msgstr "# `for` 표현식"
|
|
|
|
#: src/control-flow/for-expressions.md:3
|
|
msgid ""
|
|
"The `for` expression is closely related to the `while let` expression. It will\n"
|
|
"automatically call `into_iter()` on the expression and then iterate over it:"
|
|
msgstr "`for`표현식은 `while let` 표현식과 매우 유사합니다. `for`표현식은 자동으로 `into_iter()`를 호출한 다음 이를 반복합니다:"
|
|
|
|
#: src/control-flow/for-expressions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
"\n"
|
|
" for x in v {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" }\n"
|
|
" \n"
|
|
" for i in (0..10).step_by(2) {\n"
|
|
" println!(\"i: {i}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
"\n"
|
|
" for x in v {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" }\n"
|
|
" \n"
|
|
" for i in (0..10).step_by(2) {\n"
|
|
" println!(\"i: {i}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/control-flow/for-expressions.md:20
|
|
msgid "You can use `break` and `continue` here as usual."
|
|
msgstr "다른 언어와 마찬가지로 `break` 와 `continue`를 사용할 수 있습니다."
|
|
|
|
#: src/control-flow/for-expressions.md:24
|
|
msgid ""
|
|
"* Index iteration is not a special syntax in Rust for just that case.\n"
|
|
"* `(0..10)` is a range that implements an `Iterator` trait. \n"
|
|
"* `step_by` is a method that returns another `Iterator` that skips every other element. \n"
|
|
"* Modify the elements in the vector and explain the compiler errors. Change vector `v` to be mutable and the for loop to `for x in v.iter_mut()`."
|
|
msgstr ""
|
|
"* 러스트는 인덱스 반복을 위해 별도의 문법을 사용하지 않습니다. \n"
|
|
"* `(0..10)`은 `Iterator` 트레잇을 구현하는 범위(range) 값입니다. \n"
|
|
"* `step_by`는 반복자의 요소들을 건너뛰는 또다른 `Iterator`를 반환하는 메서드입니다.\n"
|
|
"* 벡터 요소들을 수정하려고 하면 나오는 컴파일러 에러를 같이 살펴보세요. `v` 벡터를 가변 변수로 변경하고 루프는 `for x in v.iter_mut()`로 수정하세요."
|
|
|
|
#: src/control-flow/loop-expressions.md:1
|
|
msgid "# `loop` expressions"
|
|
msgstr "# `loop` 표현식"
|
|
|
|
#: src/control-flow/loop-expressions.md:3
|
|
msgid ""
|
|
"Finally, there is a `loop` keyword which creates an endless loop. Here you must\n"
|
|
"either `break` or `return` to stop the loop:"
|
|
msgstr "마지막으로 `loop`키워드는 무한 루프를 생성합니다. 따라서 반드시 `break` 또는 `return`을 사용해서 루프를 정지해야 합니다:"
|
|
|
|
#: src/control-flow/loop-expressions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut x = 10;\n"
|
|
" loop {\n"
|
|
" x = if x % 2 == 0 {\n"
|
|
" x / 2\n"
|
|
" } else {\n"
|
|
" 3 * x + 1\n"
|
|
" };\n"
|
|
" if x == 1 {\n"
|
|
" break;\n"
|
|
" }\n"
|
|
" }\n"
|
|
" println!(\"Final x: {x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/control-flow/loop-expressions.md:25
|
|
msgid "* Break the `loop` with a value (e.g. `break 8`) and print it out."
|
|
msgstr "* `loop` 블록을 빠져나올 때 `break 8`처럼 값을 지정하고 그걸 출력해보세요."
|
|
|
|
#: src/control-flow/match-expressions.md:1
|
|
msgid "# `match` expressions"
|
|
msgstr "# `match` 표현식"
|
|
|
|
#: src/control-flow/match-expressions.md:3
|
|
msgid ""
|
|
"The `match` keyword is used to match a value against one or more patterns. In\n"
|
|
"that sense, it works like a series of `if let` expressions:"
|
|
msgstr "`match`키워드는 어떤 값을 하나 이상의 패턴에 대해 매치하는데 사용합니다. 그런 면에서 `if let` 표현식을 여러개 이어 놓은 것과 같습니다:"
|
|
|
|
#: src/control-flow/match-expressions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" match std::env::args().next().as_deref() {\n"
|
|
" Some(\"cat\") => println!(\"Will do cat things\"),\n"
|
|
" Some(\"ls\") => println!(\"Will ls some files\"),\n"
|
|
" Some(\"mv\") => println!(\"Let's move some files\"),\n"
|
|
" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n"
|
|
" None => println!(\"Hmm, no program name?\"),\n"
|
|
" _ => println!(\"Unknown program name!\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" match std::env::args().next().as_deref() {\n"
|
|
" Some(\"cat\") => println!(\"Will do cat things\"),\n"
|
|
" Some(\"ls\") => println!(\"Will ls some files\"),\n"
|
|
" Some(\"mv\") => println!(\"Let's move some files\"),\n"
|
|
" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n"
|
|
" None => println!(\"Hmm, no program name?\"),\n"
|
|
" _ => println!(\"Unknown program name!\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/control-flow/match-expressions.md:19
|
|
msgid ""
|
|
"Like `if let`, each match arm must have the same type. The type is the last\n"
|
|
"expression of the block, if any. In the example above, the type is `()`."
|
|
msgstr "`if let`과 마찬가지로 매치의 모든 팔(arm)은 같은 타입이어야 합니다. 팔이 블록이라면 블록의 마지막 표현식이 그 타입이 됩니다. 위의 예제에서 매치 표현식의 타입은 `()`입니다."
|
|
|
|
#: src/control-flow/match-expressions.md:27
|
|
msgid ""
|
|
"* Save the match expression to a variable and print it out.\n"
|
|
"* Remove `.as_deref()` and explain the error.\n"
|
|
" * `std::env::args().next()` returns an `Option<String>`, but we cannot match against `String`.\n"
|
|
" * `as_deref()` transforms an `Option<T>` to `Option<&T::Target>`. In our case, this turns `Option<String>` into `Option<&str>`.\n"
|
|
" * We can now use pattern matching to match against the `&str` inside `Option`."
|
|
msgstr ""
|
|
"* `match` 표현식을 변수에 할당하고 그 값을 출력해보세요.\n"
|
|
"* `.as_deref()`를 지워보고, 이 때 나오는 에러를 설명해주세요.\n"
|
|
" * `std::env::args().next()`는 `Option<String>` 값을 반환하는데, `String`은 직접 매치할 수 없습니다.\n"
|
|
" * `as_deref()`는 `Option<T>`를 `Option<&T::Target>`으로 바꿔줍니다. 이 경우는 `Option<String>`에서 `Option<&str>`로 바뀝니다.\n"
|
|
" * 이제는 패턴 매칭으로 `Option` 안의 `&str`을 매치할 수 있습니다."
|
|
|
|
#: src/control-flow/break-continue.md:1
|
|
msgid "# `break` and `continue`"
|
|
msgstr "# `break`와 `continue`"
|
|
|
|
#: src/control-flow/break-continue.md:3
|
|
msgid ""
|
|
"If you want to exit a loop early, use `break`, if you want to immediately start\n"
|
|
"the next iteration use `continue`. Both `continue` and `break` can optionally\n"
|
|
"take a label argument which is used to break out of nested loops:"
|
|
msgstr "루프를 빠져나가려면 `break`를, 다음 반복으로 넘어가기 위해서는 `continue`를 사용합니다. 중첩 루프에서는 레이블과 함께 사용할 수 있습니다:"
|
|
|
|
#: src/control-flow/break-continue.md:7
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
" let mut iter = v.into_iter();\n"
|
|
" 'outer: while let Some(x) = iter.next() {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" let mut i = 0;\n"
|
|
" while i < x {\n"
|
|
" println!(\"x: {x}, i: {i}\");\n"
|
|
" i += 1;\n"
|
|
" if i == 3 {\n"
|
|
" break 'outer;\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
" let mut iter = v.into_iter();\n"
|
|
" 'outer: while let Some(x) = iter.next() {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" let mut i = 0;\n"
|
|
" while i < x {\n"
|
|
" println!(\"x: {x}, i: {i}\");\n"
|
|
" i += 1;\n"
|
|
" if i == 3 {\n"
|
|
" break 'outer;\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/control-flow/break-continue.md:25
|
|
msgid "In this case we break the outer loop after 3 iterations of the inner loop."
|
|
msgstr "위 예제는 내부의 `while` 루프를 3회 반복한 후 바깥 루프를 빠져나갑니다."
|
|
|
|
#: src/std.md:1
|
|
msgid "# Standard Library"
|
|
msgstr "# 표준 라이브러리"
|
|
|
|
#: src/std.md:3
|
|
msgid ""
|
|
"Rust comes with a standard library which helps establish a set of common types\n"
|
|
"used by Rust library and programs. This way, two libraries can work together\n"
|
|
"smoothly because they both use the same `String` type."
|
|
msgstr "러스트에서 제공하는 표준 라이브러리는 러스트로 라이브러리나 프로그램을 작성할 때 공통적으로 사용할 수 있는 여러 타입들을 포함하고 있습니다. 이를 통해 서로 다른 두 라이브러리라 하더라도 함께 사용하는데 큰 어려움이 없게 됩니다. 예를 들면 두 라이브러리 모두 같은 `String` 타입을 사용하기 때문입니다."
|
|
|
|
#: src/std.md:7
|
|
msgid "The common vocabulary types include:"
|
|
msgstr "일반적인 타입은 아래와 같습니다:"
|
|
|
|
#: src/std.md:9
|
|
msgid ""
|
|
"* [`Option` and `Result`](std/option-result.md) types: used for optional values\n"
|
|
" and [error handling](error-handling.md).\n"
|
|
"\n"
|
|
"* [`String`](std/string.md): the default string type used for owned data.\n"
|
|
"\n"
|
|
"* [`Vec`](std/vec.md): a standard extensible vector.\n"
|
|
"\n"
|
|
"* [`HashMap`](std/hashmap.md): a hash map type with a configurable hashing\n"
|
|
" algorithm.\n"
|
|
"\n"
|
|
"* [`Box`](std/box.md): an owned pointer for heap-allocated data.\n"
|
|
"\n"
|
|
"* [`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated data."
|
|
msgstr ""
|
|
"* [`Option`과 `Result`](std/option-result.md) : 어떤 값이 있거나 없거나 하는 경우, 그리고 [오류 처리](error-handling.md)에 사용합니다.\n"
|
|
"\n"
|
|
"* [`String`](std/string.md): 기본적인 문자열 타입으로, 문자열 데이터를 소유하는 경우에 사용합니다.\n"
|
|
"\n"
|
|
"* [`Vec`](std/vec.md): 가변 크기의 표준 벡터 타입입니다.\n"
|
|
"\n"
|
|
"* [`HashMap`](std/hashmap.md): 해시 알고리즘을 따로 지정할 수도 있는 해시맵 타입입니다.\n"
|
|
"\n"
|
|
"* [`Box`](std/box.md): 힙 데이터에 대한 소유 포인터입니다.\n"
|
|
"\n"
|
|
"* [`Rc`](std/rc.md): 힙에 할당된 데이터에 대한 참조 카운팅 공유 포인터입니다."
|
|
|
|
#: src/std.md:25
|
|
msgid ""
|
|
" * In fact, Rust contains several layers of the Standard Library: `core`, `alloc` and `std`. \n"
|
|
" * `core` includes the most basic types and functions that don't depend on `libc`, allocator or\n"
|
|
" even the presence of an operating system. \n"
|
|
" * `alloc` includes types which require a global heap allocator, such as `Vec`, `Box` and `Arc`.\n"
|
|
" * Embedded Rust applications often only use `core`, and sometimes `alloc`."
|
|
msgstr ""
|
|
" * 사실, 러스트의 표준 라이브러리는 `core`, `alloc`, `std`와 같이 계층(layer)으로 나눠집니다.\n"
|
|
" * `core`는 `libc`나 할당자(allocator), 심지어 OS에도 의존하지 않는 가장 기본적인 함수와 타입을 포함합니다.\n"
|
|
" * `alloc`은 `Vec`, `Box`, `Arc`와 같이 전역 힙 할당이 필요한 타입을 포함합니다.\n"
|
|
" * 임베디드 러스트 응용프로그램은 주로 `core`만 사용하거나 가끔 `alloc`을 함께 사용합니다."
|
|
|
|
#: src/std/option-result.md:1
|
|
msgid "# `Option` and `Result`"
|
|
msgstr "# `Option`과 `Result`"
|
|
|
|
#: src/std/option-result.md:3
|
|
msgid "The types represent optional data:"
|
|
msgstr "이 타입은 선택적 데이터를 나타냅니다:"
|
|
|
|
#: src/std/option-result.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let numbers = vec![10, 20, 30];\n"
|
|
" let first: Option<&i8> = numbers.first();\n"
|
|
" println!(\"first: {first:?}\");\n"
|
|
"\n"
|
|
" let idx: Result<usize, usize> = numbers.binary_search(&10);\n"
|
|
" println!(\"idx: {idx:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/std/option-result.md:18
|
|
msgid ""
|
|
"* `Option` and `Result` are widely used not just in the standard library.\n"
|
|
"* `Option<&T>` has zero space overhead compared to `&T`.\n"
|
|
"* `Result` is the standard type to implement error handling as we will see on Day 3.\n"
|
|
"* `binary_search` returns `Result<usize, usize>`.\n"
|
|
" * If found, `Result::Ok` holds the index where the element is found.\n"
|
|
" * Otherwise, `Result::Err` contains the index where such an element should be inserted."
|
|
msgstr ""
|
|
"* `Option`과 `Result`는 표준 라이브러리뿐만아니라 매우 광범위하게 사용되는 타입입니다.\n"
|
|
"* `Option<&T>` 는 `&T`에 비해 공간 오버헤드가 없습니다.\n"
|
|
"* `Result`는 오류 처리를 위한 표준 타입입니다. 3일차 과정에서 살펴봅니다.\n"
|
|
"* `binary_search`는 `Result<usize, usize>`를 반환합니다.\n"
|
|
" * 요소가 발견된다면, `Result::Ok`는 발견된 요소의 인덱스를 보유합니다.\n"
|
|
" * 아니면, `Result::Err`에는 요소가 삽입되야 하는 인덱스가 포함되어 있습니다."
|
|
|
|
#: src/std/string.md:1
|
|
msgid "# String"
|
|
msgstr "# `String` 문자열"
|
|
|
|
#: src/std/string.md:3
|
|
msgid "[`String`][1] is the standard heap-allocated growable UTF-8 string buffer:"
|
|
msgstr "[`String`][1]은 힙에 할당되고 가변 길이의 표준 UTF-8 문자열 버퍼입니다:"
|
|
|
|
#: src/std/string.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut s1 = String::new();\n"
|
|
" s1.push_str(\"Hello\");\n"
|
|
" println!(\"s1: len = {}, capacity = {}\", s1.len(), s1.capacity());\n"
|
|
"\n"
|
|
" let mut s2 = String::with_capacity(s1.len() + 1);\n"
|
|
" s2.push_str(&s1);\n"
|
|
" s2.push('!');\n"
|
|
" println!(\"s2: len = {}, capacity = {}\", s2.len(), s2.capacity());\n"
|
|
"\n"
|
|
" let s3 = String::from(\"🇨🇭\");\n"
|
|
" println!(\"s3: len = {}, number of chars = {}\", s3.len(),\n"
|
|
" s3.chars().count());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/std/string.md:22
|
|
msgid ""
|
|
"`String` implements [`Deref<Target = str>`][2], which means that you can call all\n"
|
|
"`str` methods on a `String`."
|
|
msgstr "`String`은 [`Deref<Target = str>`][2]을 구현합니다. 이는 , `String` 값에 대해서도 `str`의 모든 메서드를 호출 할 수 있다는 의미 입니다."
|
|
|
|
#: src/std/string.md:30
|
|
msgid ""
|
|
"* `String::new` returns a new empty string, use `String::with_capacity` when you know how much data you want to push to the string.\n"
|
|
"* `String::len` returns the size of the `String` in bytes (which can be different from its length in characters).\n"
|
|
"* `String::chars` returns an iterator over the actual characters. Note that a `char` can be different from what a human will consider a \"character\" due to [grapheme clusters](https://docs.rs/unicode-segmentation/latest/unicode_segmentation/struct.Graphemes.html).\n"
|
|
"* When people refer to strings they could either be talking about `&str` or `String`. \n"
|
|
"* When a type implements `Deref<Target = T>`, the compiler will let you transparently call methods from `T`.\n"
|
|
" * `String` implements `Deref<Target = str>` which transparently gives it access to `str`'s methods.\n"
|
|
" * Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;.\n"
|
|
"* `String` is implemented as a wrapper around a vector of bytes, many of the operations you see supported on vectors are also supported on `String`, but with some extra guarantees.\n"
|
|
"* Compare the different ways to index a `String` by using `s3[i]` and `s3.chars().nth(i).unwrap()` where `i` is in-bound, out-of-bounds, and \"on\" the flag Unicode character."
|
|
msgstr ""
|
|
"* `String::new`는 새로운 빈 문자열을 반환합니다. `String::with_capacity`는 새로 만들 문자열 버퍼에 넣을 데이터 크기를 알고 있는 경우에 사용할 수 있습니다.\n"
|
|
"* `String::len`은 `String`의 바이트 크기를 반환합니다. (실제 문자 개수와는 다를 수 있습니다.)\n"
|
|
"* `String::chars`는 실제 문자(character)들에 대한 이터레이터를 반환합니다. `char`로 표현되는 문자는 우리가 실제로 인식하고 사용하는 문자와는 다를 수 있습니다. 자소 결합으로 문자를 표현하는 경우가 있기 때문입니다. 이에 대해서는 [Grapheme Cluster](https://docs.rs/unicode-segmentation/latest/unicode_segmentation/struct.Graphemes.html)를 참고하세요.\n"
|
|
"* 문자열이라고 언급하는 경우는 `&str`이거나 `String`일 수 있습니다.\n"
|
|
"* 어떤 타입이 `Deref<Target = T>`를 구현하고 있으면, 컴파일러는 여러분이 `T`의 메소드들을 호출할 수 있게 도와줍니다.\n"
|
|
" * `String`은 `Deref<Target = str>`을 구현하고 있기 때문에 `String`에 대해서도 `str` 메소드들을 호출할 수 있습니다.\n"
|
|
" * `let s3 = s1.deref();`와 `let s3 = &*s1;`을 비교해보세요.\n"
|
|
"* `String`은 바이트 벡터의 래퍼로 구현되어 있습니다. 벡터가 지원하는 여러가지 연산들을 `String`도 지원합니다. 다만 `String`은 몇가지 보장 내용이 더 있습니다.\n"
|
|
"* `String`을 인덱스로 접근하는 방법들을 비교해보세요. `s3[i]`와 `s3.chars().nth(i).unwrap()` 방법에 대해 `i`값이 범위를 벗어날 때, 벗어나지 않을 때, 혹은 플래그 유니코드 문자에 딱 걸린 경우 등을 비교 설명해주세요."
|
|
|
|
#: src/std/vec.md:1
|
|
msgid "# `Vec`"
|
|
msgstr "# `Vec`"
|
|
|
|
#: src/std/vec.md:3
|
|
msgid "[`Vec`][1] is the standard resizable heap-allocated buffer:"
|
|
msgstr "[`Vec`][1] 는 힙에 할당된 표준 가변 크기 버퍼입니다:"
|
|
|
|
#: src/std/vec.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut v1 = Vec::new();\n"
|
|
" v1.push(42);\n"
|
|
" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n"
|
|
"\n"
|
|
" let mut v2 = Vec::with_capacity(v1.len() + 1);\n"
|
|
" v2.extend(v1.iter());\n"
|
|
" v2.push(9999);\n"
|
|
" println!(\"v2: len = {}, capacity = {}\", v2.len(), v2.capacity());\n"
|
|
"\n"
|
|
" // Canonical macro to initialize a vector with elements.\n"
|
|
" let mut v3 = vec![0, 0, 1, 2, 3, 4];\n"
|
|
"\n"
|
|
" // Retain only the even elements.\n"
|
|
" v3.retain(|x| x % 2 == 0);\n"
|
|
" println!(\"{v3:?}\");\n"
|
|
"\n"
|
|
" // Remove consecutive duplicates.\n"
|
|
" v3.dedup();\n"
|
|
" println!(\"{v3:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/std/vec.md:29
|
|
msgid ""
|
|
"`Vec` implements [`Deref<Target = [T]>`][2], which means that you can call slice\n"
|
|
"methods on a `Vec`."
|
|
msgstr "`Vec`은 [`Deref<Target = [T]>`][2]를 구현합니다. 이는 `Vec`에서 슬라이스 메서드를 호출 할 수 있다는 의미입니다."
|
|
|
|
#: src/std/vec.md:37
|
|
msgid ""
|
|
"* `Vec` is a type of collection, along with `String` and `HashMap`. The data it contains is stored\n"
|
|
" on the heap. This means the amount of data doesn't need to be known at compile time. It can grow\n"
|
|
" or shrink at runtime.\n"
|
|
"* Notice how `Vec<T>` is a generic type too, but you don't have to specify `T` explicitly. As always\n"
|
|
" with Rust type inference, the `T` was established during the first `push` call.\n"
|
|
"* `vec![...]` is a canonical macro to use instead of `Vec::new()` and it supports adding initial\n"
|
|
" elements to the vector.\n"
|
|
"* To index the vector you use `[` `]`, but they will panic if out of bounds. Alternatively, using\n"
|
|
" `get` will return an `Option`. The `pop` function will remove the last element.\n"
|
|
"* Show iterating over a vector and mutating the value:\n"
|
|
" `for e in &mut v { *e += 50; }`"
|
|
msgstr ""
|
|
"* `Vec`은 `String`이나 `HashMap`과 같은 컬렉션 타입입니다. 벡터는 데이터를 힙에 저장합니다. 이는 컴파일 시점에 데이터 크기를 알 필요가 없다는 의미고, 런타임에 더 커질 수도 작아질 수도 있습니다.\n"
|
|
"* `Vec<T>`는 제네릭 타입이기도 합니다. 하지만 `T`를 꼭 지정해줄 필요는 없습니다. 이 경우, 러스트 타입 추론이 벡터에 처음 `push`하는 데이터로 `T`를 알 수 있었습니다.\n"
|
|
"* `vec![...]`는 `Vec::new()` 대신 쓸 수 있는 표준 매크로로서, 초기 데이터를 추가한 벡터를 생성할 수 있습니다.\n"
|
|
"* 벡터는 `[` `]`를 사용하여 인덱스로 접근할 수 있습니다. 하지만 범위를 벗어나면 패닉이 발생합니다. 대신 `get`을 사용하면 `Option`을 반환합니다. `pop` 함수는 마지막 요소를 제거합니다.\n"
|
|
"* 벡터를 순회하면서 값을 변경할 수도 있음을 보여주세요: `for e in &mut v { *e += 50; }`"
|
|
|
|
#: src/std/hashmap.md:1
|
|
msgid "# `HashMap`"
|
|
msgstr "# `HashMap`"
|
|
|
|
#: src/std/hashmap.md:3
|
|
msgid "Standard hash map with protection against HashDoS attacks:"
|
|
msgstr "HashDoS 공격으로부터 보호되는 표준 해시 맵입니다:"
|
|
|
|
#: src/std/hashmap.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::collections::HashMap;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut page_counts = HashMap::new();\n"
|
|
" page_counts.insert(\"Adventures of Huckleberry Finn\".to_string(), 207);\n"
|
|
" page_counts.insert(\"Grimms' Fairy Tales\".to_string(), 751);\n"
|
|
" page_counts.insert(\"Pride and Prejudice\".to_string(), 303);\n"
|
|
"\n"
|
|
" if !page_counts.contains_key(\"Les Misérables\") {\n"
|
|
" println!(\"We know about {} books, but not Les Misérables.\",\n"
|
|
" page_counts.len());\n"
|
|
" }\n"
|
|
"\n"
|
|
" for book in [\"Pride and Prejudice\", \"Alice's Adventure in Wonderland\"] {\n"
|
|
" match page_counts.get(book) {\n"
|
|
" Some(count) => println!(\"{book}: {count} pages\"),\n"
|
|
" None => println!(\"{book} is unknown.\")\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" // Use the .entry() method to insert a value if nothing is found.\n"
|
|
" for book in [\"Pride and Prejudice\", \"Alice's Adventure in Wonderland\"] {\n"
|
|
" let page_count: &mut i32 = page_counts.entry(book.to_string()).or_insert(0);\n"
|
|
" *page_count += 1;\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"{page_counts:#?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/std/hashmap.md:38
|
|
msgid ""
|
|
"* `HashMap` is not defined in the prelude and needs to be brought into scope.\n"
|
|
"* Try the following lines of code. The first line will see if a book is in the hashmap and if not return an alternative value. The second line will insert the alternative value in the hashmap if the book is not found.\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let pc1 = page_counts\n"
|
|
" .get(\"Harry Potter and the Sorcerer's Stone \")\n"
|
|
" .unwrap_or(&336);\n"
|
|
" let pc2 = page_counts\n"
|
|
" .entry(\"The Hunger Games\".to_string())\n"
|
|
" .or_insert(374);\n"
|
|
" ```\n"
|
|
"* Unlike `vec!`, there is unfortunately no standard `hashmap!` macro.\n"
|
|
" * Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`][1], which allows us to easily initialize a hash map from a literal array:\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let page_counts = HashMap::from([\n"
|
|
" (\"Harry Potter and the Sorcerer's Stone\".to_string(), 336),\n"
|
|
" (\"The Hunger Games\".to_string(), 374),\n"
|
|
" ]);\n"
|
|
" ```\n"
|
|
"\n"
|
|
" * Alternatively HashMap can be built from any `Iterator` which yields key-value tuples.\n"
|
|
"* We are showing `HashMap<String, i32>`, and avoid using `&str` as key to make examples easier. Using references in collections can, of course, be done,\n"
|
|
" but it can lead into complications with the borrow checker.\n"
|
|
" * Try removing `to_string()` from the example above and see if it still compiles. Where do you think we might run into issues?"
|
|
msgstr ""
|
|
"* `HashMap`은 prelude에 정의되어 있지 않기 때문에 명시적으로 추가해줘야 합니다.\n"
|
|
"* 아래 코드를 테스트해보세요. 첫 문장에서는 해시맵에 책이 있는지 검사하여, 없으면 디폴트 값을 반환합니다. 두번 째 문장에서는 해시맵에 해당 책이 없는 경우, 지정한 값을 해시맵에 추가한 뒤 그 값을 반환합니다.\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let pc1 = page_counts\n"
|
|
" .get(\"Harry Potter and the Sorcerer's Stone \")\n"
|
|
" .unwrap_or(&336);\n"
|
|
" let pc2 = page_counts\n"
|
|
" .entry(\"The Hunger Games\".to_string())\n"
|
|
" .or_insert(374);\n"
|
|
" ```\n"
|
|
"* 안타깝지만 `hashmap!`같은 매크로가 없습니다.\n"
|
|
" * 러스트 1.56부터는 `HashMap`이 [`From<[(K, V); N]>`][1]을 구현하기 때문에 배열 리터럴을 이용하여 쉽게 해시맵을 초기화할 수 있습니다:\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" let page_counts = HashMap::from([\n"
|
|
" (\"Harry Potter and the Sorcerer's Stone\".to_string(), 336),\n"
|
|
" (\"The Hunger Games\".to_string(), 374),\n"
|
|
" ]);\n"
|
|
" ```\n"
|
|
"\n"
|
|
" * 키-값 쌍에 대한 `Iterator`로 해시맵을 만들 수도 있습니다.\n"
|
|
"* 예제 코드에서는 편의상 해시맵의 키로 `&str`를 사용하지 않았습니다. 물론 컬렉션에 참조를 사용할 수도 있습니다. 다만 참조를 사용하게 되면 빌림 검사기 때문에 복잡해 질 수 있습니다.\n"
|
|
" * 예제 코드에서 `to_string()`을 없애도 컴파일에 문제가 없는지 확인해보세요. 어떤 문제에 부딪힐까요?"
|
|
|
|
#: src/std/box.md:1
|
|
msgid "# `Box`"
|
|
msgstr "# `Box`"
|
|
|
|
#: src/std/box.md:3
|
|
msgid "[`Box`][1] is an owned pointer to data on the heap:"
|
|
msgstr "[`Box`][1]는 힙 데이터에 대한 소유 포인터입니다:"
|
|
|
|
#: src/std/box.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let five = Box::new(5);\n"
|
|
" println!(\"five: {}\", *five);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let five = Box::new(5);\n"
|
|
" println!(\"five: {}\", *five);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/std/box.md:13
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - -. .- - - - - - -.\n"
|
|
": : : :\n"
|
|
": five : : :\n"
|
|
": +-----+ : : +-----+ :\n"
|
|
": | o---|---+-----+-->| 5 | :\n"
|
|
": +-----+ : : +-----+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"`- - - - - - -' `- - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
"```bob\n"
|
|
" 스택 힙\n"
|
|
".- - - - - - -. .- - - - - - -.\n"
|
|
": : : :\n"
|
|
": five : : :\n"
|
|
": +-----+ : : +-----+ :\n"
|
|
": | o---|---+-----+-->| 5 | :\n"
|
|
": +-----+ : : +-----+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"`- - - - - - -' `- - - - - - -'\n"
|
|
"```"
|
|
|
|
#: src/std/box.md:26
|
|
msgid ""
|
|
"`Box<T>` implements `Deref<Target = T>`, which means that you can [call methods\n"
|
|
"from `T` directly on a `Box<T>`][2]."
|
|
msgstr "`Box<T>`은 [`Deref<Target = T>`][2]를 구현합니다. 이는 [`Box<T>`에서 `T` 메서드를 직접 호출][2] 할 수 있다는 의미입니다."
|
|
|
|
#: src/std/box.md:34
|
|
msgid ""
|
|
"* `Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be not null. \n"
|
|
"* In the above example, you can even leave out the `*` in the `println!` statement thanks to `Deref`. \n"
|
|
"* A `Box` can be useful when you:\n"
|
|
" * have a type whose size that can't be known at compile time, but the Rust compiler wants to know an exact size.\n"
|
|
" * want to transfer ownership of a large amount of data. To avoid copying large amounts of data on the stack, instead store the data on the heap in a `Box` so only the pointer is moved."
|
|
msgstr ""
|
|
"* `Box`는 C++의 `std::unique_ptr`과 비슷합니다. 차이라면 `Box`는 널이 아님을 보장한다는 점입니다.\n"
|
|
"* `Deref` 덕분에 위 예제의 `println!`문에 사용된 `*`를 빼도 문제가 없습니다.\n"
|
|
"* `Box`는 아래의 경우에 유용합니다:\n"
|
|
" * 타입 크기를 컴파일 시점에 알 수 없는 경우.\n"
|
|
" * 아주 큰 데이터의 소유권을 전달하고 싶은 경우. 스택에 있는 큰 데이터를 복사하는 대신 `Box`를 이용하여 데이터는 힙에 저장하고 포인터만 이동하면 됩니다."
|
|
|
|
#: src/std/box-recursive.md:1
|
|
msgid "# Box with Recursive Data Structures"
|
|
msgstr "# 재귀자료 구조에서의 `Box`"
|
|
|
|
#: src/std/box-recursive.md:3
|
|
msgid "Recursive data types or data types with dynamic sizes need to use a `Box`:"
|
|
msgstr "재귀 데이터나 동적크기의 데이터 타입은 `Box`타입을 사용해야 합니다:"
|
|
|
|
#: src/std/box-recursive.md:5 src/std/box-niche.md:3
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"enum List<T> {\n"
|
|
" Cons(T, Box<List<T>>),\n"
|
|
" Nil,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));\n"
|
|
" println!(\"{list:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/std/box-recursive.md:18
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": list : : :\n"
|
|
": +------+----+----+ : : +------+----+----+ +------+----+----+ :\n"
|
|
": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // | :\n"
|
|
": +------+----+----+ : : +------+----+----+ +------+----+----+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
"```bob\n"
|
|
" 스택 힙\n"
|
|
".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": list : : :\n"
|
|
": +------+----+----+ : : +------+----+----+ +------+----+----+ :\n"
|
|
": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // | :\n"
|
|
": +------+----+----+ : : +------+----+----+ +------+----+----+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
|
|
#: src/std/box-recursive.md:33
|
|
msgid ""
|
|
"* If the `Box` was not used here and we attempted to embed a `List` directly into the `List`,\n"
|
|
"the compiler would not compute a fixed size of the struct in memory, it would look infinite.\n"
|
|
"\n"
|
|
"* `Box` solves this problem as it has the same size as a regular pointer and just points at the next\n"
|
|
"element of the `List` in the heap.\n"
|
|
"\n"
|
|
"* Remove the `Box` in the List definition and show the compiler error. \"Recursive with indirection\" is a hint you might want to use a Box or reference of some kind, instead of storing a value directly. \n"
|
|
" "
|
|
msgstr ""
|
|
"* 만일 `Box`를 사용하지 않고 `List`에 직접 `List`를 포함하려고 시도한다면, 컴파일러는 구조체의 고정 크기를 계산할 수 없습니다. 컴파일러가 보기에 무한대의 크기로 보일 것입니다.\n"
|
|
"\n"
|
|
"* `Box`는 일반 포인터와 크기가 같기 때문에 크기를 계산하는 데 문제가 없습니다. 다만 힙에 위치한 `List`의 다음 요소를 가리킬 뿐입니다.\n"
|
|
"\n"
|
|
"* `List` 정의에서 `Box`를 제거하면 어떤 컴파일러 에러가 나오는지 같이 살펴보세요. “Recursive with indirection”라는 메시지를 보면, 값을 직접 저장하는 대신 `Box`나 비슷한 다른 종류의 참조 타입이 필요하다는 힌트를 얻을 수 있습니다."
|
|
|
|
#: src/std/box-niche.md:1
|
|
msgid "# Niche Optimization"
|
|
msgstr "# 틈새 최적화(Niche Optimization)"
|
|
|
|
#: src/std/box-niche.md:16
|
|
msgid ""
|
|
"A `Box` cannot be empty, so the pointer is always valid and non-`null`. This\n"
|
|
"allows the compiler to optimize the memory layout:"
|
|
msgstr "`Box`는 비어있을 수 없습니다. 따라서 포인터는 항상 유효하며 `null`이 아닙니다. 이는 컴파일러가 메모리 레이아웃을 최적화 할 수 있게 해줍니다:"
|
|
|
|
#: src/std/box-niche.md:19
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": list : : :\n"
|
|
": +----+----+ : : +----+----+ +----+------+ :\n"
|
|
": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null | :\n"
|
|
": +----+----+ : : +----+----+ +----+------+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
"```bob\n"
|
|
" 스택 힙\n"
|
|
".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": list : : :\n"
|
|
": +----+----+ : : +----+----+ +----+------+ :\n"
|
|
": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null | :\n"
|
|
": +----+----+ : : +----+----+ +----+------+ :\n"
|
|
": : : :\n"
|
|
": : : :\n"
|
|
"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
|
|
#: src/std/rc.md:1
|
|
msgid "# `Rc`"
|
|
msgstr "# `Rc`"
|
|
|
|
#: src/std/rc.md:3
|
|
msgid ""
|
|
"[`Rc`][1] is a reference-counted shared pointer. Use this when you need to refer\n"
|
|
"to the same data from multiple places:"
|
|
msgstr "[`Rc`][1]는 참조 카운팅 공유 포인터입니다. 여러 위치에서 동일한 데이터를 참조해야할 경우 사용합니다:"
|
|
|
|
#: src/std/rc.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::rc::Rc;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut a = Rc::new(10);\n"
|
|
" let mut b = a.clone();\n"
|
|
"\n"
|
|
" println!(\"a: {a}\");\n"
|
|
" println!(\"b: {b}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"use std::rc::Rc;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut a = Rc::new(10);\n"
|
|
" let mut b = a.clone();\n"
|
|
"\n"
|
|
" println!(\"a: {a}\");\n"
|
|
" println!(\"b: {b}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/std/rc.md:18
|
|
msgid ""
|
|
"* If you need to mutate the data inside an `Rc`, you will need to wrap the data in\n"
|
|
" a type such as [`Cell` or `RefCell`][2].\n"
|
|
"* See [`Arc`][3] if you are in a multi-threaded context.\n"
|
|
"* You can *downgrade* a shared pointer into a [`Weak`][4] pointer to create cycles\n"
|
|
" that will get dropped."
|
|
msgstr ""
|
|
"* `Rc`내부의 데이터를 변경해야 하는 경우, 데이터를 [`Cell` 또는 `RefCell`][2]로 래핑해야합니다.\n"
|
|
"* 멀티스레드인 경우 [`Arc`][3]를 참조하시기 바랍니다.\n"
|
|
"* drop 가능한 순환 구조를 만들기 위해 공유 포인터를 [`Weak`][4] 포인터로 **다운그레이드**할 수도 있습니다."
|
|
|
|
#: src/std/rc.md:31
|
|
msgid ""
|
|
"* `Rc`'s Count ensures that its contained value is valid for as long as there are references.\n"
|
|
"* Like C++'s `std::shared_ptr`.\n"
|
|
"* `clone` is cheap: it creates a pointer to the same allocation and increases the reference count. Does not make a deep clone and can generally be ignored when looking for performance issues in code.\n"
|
|
"* `make_mut` actually clones the inner value if necessary (\"clone-on-write\") and returns a mutable reference.\n"
|
|
"* Use `Rc::strong_count` to check the reference count.\n"
|
|
"* Compare the different datatypes mentioned. `Box` enables (im)mutable borrows that are enforced at compile time. `RefCell` enables (im)mutable borrows that are enforced at run time and will panic if it fails at runtime.\n"
|
|
"* You can `downgrade()` a `Rc` into a *weakly reference-counted* object to\n"
|
|
" create cycles that will be dropped properly (likely in combination with\n"
|
|
" `RefCell`)."
|
|
msgstr ""
|
|
"* `Rc`는 참조 카운트를 통해 참조가 있는 동안은 `Rc`가 가리키고 있는 값이 메모리에서 해제되지 않음을 보장합니다.\n"
|
|
"* C++의 `std::shared_ptr`와 유사합니다.\n"
|
|
"* `clone`은 저렴합니다. 같은 곳을 가리키는 포인터를 하나 더 만들고, 참조 카운트를 늘립니다. 포인터가 가리키는 값 자체가 복제(깊은 복제)되지는 않으며, 그래서 코드에서 성능 문제가 있는지 검토할 때 일반적으로 `Rc`를 `clone`하는 것은 무시할 수 있습니다.\n"
|
|
"* `make_mut`는 실제로 필요한 경우에 내부 값을 복제하고(\"clone-on-write\") 가변 참조를 반환합니다.\n"
|
|
"* 참조 카운트를 확인하려면 `Rc::strong_count`를 사용하세요.\n"
|
|
"* 여기 언급된 몇가지 데이터 타입을 비교해보세요. `Box`는 불변/가변 빌림(borrow)을 컴파일 시점에 강제합니다. `RefCell`은 실행 시점에 불변/가변 빌림을 강제하며, 만약 런타임 시에 빌림 문제가 발생하면 패닉을 일으킵니다.\n"
|
|
"* `Rc`는 `downgrade()`로 다운그레이드하여 **weak 참조 카운트**로 변경할 수 있습니다. 그러면 순환구조라 하더라도 drop이 가능합니다. (아마도 `RefCell` 을 함께 사용해야 할 것입니다.)"
|
|
|
|
#: src/std/rc.md:41
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::rc::{Rc, Weak};\n"
|
|
"use std::cell::RefCell;\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Node {\n"
|
|
" value: i64,\n"
|
|
" parent: Option<Weak<RefCell<Node>>>,\n"
|
|
" children: Vec<Rc<RefCell<Node>>>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut root = Rc::new(RefCell::new(Node {\n"
|
|
" value: 42,\n"
|
|
" parent: None,\n"
|
|
" children: vec![],\n"
|
|
" }));\n"
|
|
" let child = Rc::new(RefCell::new(Node {\n"
|
|
" value: 43,\n"
|
|
" children: vec![],\n"
|
|
" parent: Some(Rc::downgrade(&root))\n"
|
|
" }));\n"
|
|
" root.borrow_mut().children.push(child);\n"
|
|
"\n"
|
|
" println!(\"graph: {root:#?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/modules.md:1
|
|
msgid "# Modules"
|
|
msgstr "# 모듈"
|
|
|
|
#: src/modules.md:3
|
|
msgid "We have seen how `impl` blocks let us namespace functions to a type."
|
|
msgstr "`impl`블록은 해당 타입의 함수들에 대한 네임스페이스를 제공합니다."
|
|
|
|
#: src/modules.md:5
|
|
msgid "Similarly, `mod` lets us namespace types and functions:"
|
|
msgstr "마찬가지로, `mod`는 타입과 함수들에 대해 네임스페이스를 제공합니다:"
|
|
|
|
#: src/modules.md:7
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"mod foo {\n"
|
|
" pub fn do_something() {\n"
|
|
" println!(\"In the foo module\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"mod bar {\n"
|
|
" pub fn do_something() {\n"
|
|
" println!(\"In the bar module\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" foo::do_something();\n"
|
|
" bar::do_something();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/modules.md:28
|
|
msgid ""
|
|
"* Packages provide functionality and include a `Cargo.toml` file that describes how to build a bundle of 1+ crates.\n"
|
|
"* Crates are a tree of modules, where a binary crate creates an executable and a library crate compiles to a library.\n"
|
|
"* Modules define organization, scope, and are the focus of this section."
|
|
msgstr ""
|
|
"* 패키지는 기능을 제공하며 하나의 대표 `Cargo.toml` 파일을 포함합니다. 패키지를 구성하는 크레이트들을 빌드하는 방법이 이 파일에 기술됩니다.\n"
|
|
"* 크레이트는 모듈의 트리입니다. 바이너리 크레이트는 실행파일로 빌드되고, 라이브러리 크레이트는 라이브러리로 빌드됩니다.\n"
|
|
"* 모듈은 코드를 조직화하고 스코프를 정의하는 단위입니다."
|
|
|
|
#: src/modules/visibility.md:1
|
|
msgid "# Visibility"
|
|
msgstr "# 가시성"
|
|
|
|
#: src/modules/visibility.md:3
|
|
msgid "Modules are a privacy boundary:"
|
|
msgstr "모듈의 타입이나 함수는 기본적으로 바깥에 노출되지 않습니다:"
|
|
|
|
#: src/modules/visibility.md:5
|
|
msgid ""
|
|
"* Module items are private by default (hides implementation details).\n"
|
|
"* Parent and sibling items are always visible.\n"
|
|
"* In other words, if an item is visible in module `foo`, it's visible in all the\n"
|
|
" descendants of `foo`."
|
|
msgstr ""
|
|
"* 따라서 모듈의 세부 구현 내용이 감춰집니다.\n"
|
|
"* 부모와 이웃 항목은 언제나 접근 가능합니다.\n"
|
|
"* 즉, 모듈 `foo`에서 접근 가능한 항목이라면 `foo` 아래의 모든 모듈에서 접근가능합니다."
|
|
|
|
#: src/modules/visibility.md:10
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"mod outer {\n"
|
|
" fn private() {\n"
|
|
" println!(\"outer::private\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn public() {\n"
|
|
" println!(\"outer::public\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" mod inner {\n"
|
|
" fn private() {\n"
|
|
" println!(\"outer::inner::private\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn public() {\n"
|
|
" println!(\"outer::inner::public\");\n"
|
|
" super::private();\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" outer::public();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/modules/visibility.md:39
|
|
msgid "* Use the `pub` keyword to make modules public."
|
|
msgstr "* `pub` 키워드는 모듈에도 사용할 수 있습니다."
|
|
|
|
#: src/modules/visibility.md:41
|
|
msgid "Additionally, there are advanced `pub(...)` specifiers to restrict the scope of public visibility."
|
|
msgstr "또한, 고급 기능으로 `pub(...)` 지정자를 사용하여 공개 범위를 제한할 수 있습니다."
|
|
|
|
#: src/modules/visibility.md:43
|
|
msgid ""
|
|
"* See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself).\n"
|
|
"* Configuring `pub(crate)` visibility is a common pattern.\n"
|
|
"* Less commonly, you can give visibility to a specific path.\n"
|
|
"* In any case, visibility must be granted to an ancestor module (and all of its descendants)."
|
|
msgstr ""
|
|
"* [공식 문서](https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)를 참고하세요.\n"
|
|
"* `pub(crate)`로 가시성을 지정하는 것이 자주 쓰입니다.\n"
|
|
"* 자주 쓰이진 않지만 특정 경로에 대해서만 가시성을 부여할 수 있습니다.\n"
|
|
"* 어떤 경우이든 가시성이 부여되면 해당 모듈을 포함하여 하위의 모든 모듈이 적용받습니다."
|
|
|
|
#: src/modules/paths.md:1
|
|
msgid "# Paths"
|
|
msgstr "# 경로"
|
|
|
|
#: src/modules/paths.md:3
|
|
msgid "Paths are resolved as follows:"
|
|
msgstr "경로는 아래와 같이 구분합니다:"
|
|
|
|
#: src/modules/paths.md:5
|
|
msgid ""
|
|
"1. As a relative path:\n"
|
|
" * `foo` or `self::foo` refers to `foo` in the current module,\n"
|
|
" * `super::foo` refers to `foo` in the parent module.\n"
|
|
"\n"
|
|
"2. As an absolute path:\n"
|
|
" * `crate::foo` refers to `foo` in the root of the current crate,\n"
|
|
" * `bar::foo` refers to `foo` in the `bar` crate."
|
|
msgstr ""
|
|
"1. 상대 경로\n"
|
|
" * `foo` 또는 `self::foo`는 현재 모듈 내부의 `foo`를 가리킵니다.\n"
|
|
" * `super::foo`는 부모 모듈의 `foo`를 가리킵니다.\n"
|
|
"\n"
|
|
"2. 절대 경로\n"
|
|
" * `crate::foo`는 현재 크레이트 루트의 `foo`를 가리킵니다.\n"
|
|
" * `bar::foo`는 `bar`크레이트의 `foo`를 가리킵니다."
|
|
|
|
#: src/modules/filesystem.md:1
|
|
msgid "# Filesystem Hierarchy"
|
|
msgstr "# 파일시스템 계층"
|
|
|
|
#: src/modules/filesystem.md:3
|
|
msgid "The module content can be omitted:"
|
|
msgstr "모듈의 내용은 생략될 수 있습니다:"
|
|
|
|
#: src/modules/filesystem.md:5
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"mod garden;\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/modules/filesystem.md:9
|
|
msgid "The `garden` module content is found at:"
|
|
msgstr "`garden` 모듈의 내용은 아래 위치에서 확인 할 수 있습니다:"
|
|
|
|
#: src/modules/filesystem.md:11
|
|
msgid ""
|
|
"* `src/garden.rs` (modern Rust 2018 style)\n"
|
|
"* `src/garden/mod.rs` (older Rust 2015 style)"
|
|
msgstr ""
|
|
|
|
#: src/modules/filesystem.md:14
|
|
msgid "Similarly, a `garden::vegetables` module can be found at:"
|
|
msgstr "유사하게 `garden::vegetables` 모듈은 아래 위치에서 확인할 수 있습니다:"
|
|
|
|
#: src/modules/filesystem.md:16
|
|
msgid ""
|
|
"* `src/garden/vegetables.rs` (modern Rust 2018 style)\n"
|
|
"* `src/garden/vegetables/mod.rs` (older Rust 2015 style)"
|
|
msgstr ""
|
|
|
|
#: src/modules/filesystem.md:19
|
|
msgid "The `crate` root is in:"
|
|
msgstr "`crate(크레이트)`의 루트는 아래 경로 입니다:"
|
|
|
|
#: src/modules/filesystem.md:21
|
|
msgid ""
|
|
"* `src/lib.rs` (for a library crate)\n"
|
|
"* `src/main.rs` (for a binary crate)"
|
|
msgstr ""
|
|
"* `src/lib.rs` (라이브러리 크레이트)\n"
|
|
"* `src/main.rs` (바이너리 크레이트)"
|
|
|
|
#: src/modules/filesystem.md:26
|
|
msgid ""
|
|
"* The change from `module/mod.rs` to `module.rs` doesn't preclude the use of submodules in Rust 2018.\n"
|
|
" (It was mandatory in Rust 2015.)\n"
|
|
"\n"
|
|
" The following is valid:\n"
|
|
"\n"
|
|
" ```ignore\n"
|
|
" src/\n"
|
|
" ├── main.rs\n"
|
|
" ├── top_module.rs\n"
|
|
" └── top_module/\n"
|
|
" └── sub_module.rs\n"
|
|
" ```\n"
|
|
"\n"
|
|
"* The main reason for the change is to prevent many files named `mod.rs`, which can be hard\n"
|
|
" to distinguish in IDEs.\n"
|
|
"\n"
|
|
"* Rust will look for modules in `modulename/mod.rs` and `modulename.rs`, but this can be changed\n"
|
|
" with a compiler directive:\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" #[path = \"some/path.rs\"]\n"
|
|
" mod some_module { }\n"
|
|
" ```\n"
|
|
"\n"
|
|
" This is useful, for example, if you would like to place tests for a module in a file named\n"
|
|
" `some_module_test.rs`, similar to the convention in Go."
|
|
msgstr ""
|
|
"* `module/mod.rs`를 `module.rs`로 바꾼다 하더라도 Rust 2018에서는 하위 모듈을 사용할 수 있습니다. (Rust 2015에서는 하위 모듈이 있는 모듈은 `module/mod.rs` 형태여야 했습니다.)\n"
|
|
"\n"
|
|
" 아래처럼 구성하는데 아무런 문제가 없습니다:\n"
|
|
"\n"
|
|
" ```ignore\n"
|
|
" src/\n"
|
|
" ├── main.rs\n"
|
|
" ├── top_module.rs\n"
|
|
" └── top_module/\n"
|
|
" └── sub_module.rs\n"
|
|
" ```\n"
|
|
"\n"
|
|
"* 이렇게 변경된 주된 이유는 모두 똑같은 이름의 `mod.rs` 파일이 잔뜩 생기는 것을 방지하기 위해서 입니다. IDE에서는 이들을 구분하기가 까다로웠습니다.\n"
|
|
"\n"
|
|
"* 러스트는 모듈을 찾을 때 `modulename/mod.rs`와 `modulename.rs` 파일을 검사하는데, 컴파일러 디렉티브로 이를 변경할 수 있습니다.\n"
|
|
"\n"
|
|
" ```rust,ignore\n"
|
|
" #[path = \"some/path.rs\"]\n"
|
|
" mod some_module { }\n"
|
|
" ```\n"
|
|
"\n"
|
|
" Go언어 에서처럼 어떤 모듈의 테스트를 `some_module_test.rs` 같은 파일에 두고 싶은 경우에 유용합니다."
|
|
|
|
#: src/exercises/day-2/afternoon.md:1
|
|
msgid "# Day 2: Afternoon Exercises"
|
|
msgstr "# 2일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-2/afternoon.md:3
|
|
msgid "The exercises for this afternoon will focus on strings and iterators."
|
|
msgstr "이번 연습문제는 문자열과 반복자에 초점을 맞출 것입니다."
|
|
|
|
#: src/exercises/day-2/luhn.md:1
|
|
msgid "# Luhn Algorithm"
|
|
msgstr "# 룬(Luhn) 알고리즘"
|
|
|
|
#: src/exercises/day-2/luhn.md:3
|
|
msgid ""
|
|
"The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is used to\n"
|
|
"validate credit card numbers. The algorithm takes a string as input and does the\n"
|
|
"following to validate the credit card number:"
|
|
msgstr "[룬(Luhn) 알고리즘](https://ko.wikipedia.org/wiki/%EB%A3%AC_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98)은 신용카드 번호 검증에 사용되는 알고리즘 입니다. 이 알고리즘은 신용카드 번호를 `문자열`로 입력받고, 아래의 순서에 따라 신용카드 번호의 유효성을 확인합니다:"
|
|
|
|
#: src/exercises/day-2/luhn.md:7
|
|
msgid ""
|
|
"* Ignore all spaces. Reject number with less than two digits.\n"
|
|
"\n"
|
|
"* Moving from right to left, double every second digit: for the number `1234`,\n"
|
|
" we double `3` and `1`.\n"
|
|
"\n"
|
|
"* After doubling a digit, sum the digits. So doubling `7` becomes `14` which\n"
|
|
" becomes `5`.\n"
|
|
"\n"
|
|
"* Sum all the undoubled and doubled digits.\n"
|
|
"\n"
|
|
"* The credit card number is valid if the sum ends with `0`."
|
|
msgstr ""
|
|
"* 모든 공백을 무시합니다. 2자리 미만 숫자는 무시합니다.\n"
|
|
"\n"
|
|
"* 오른쪽에서 왼쪽으로 이동하며 2번째 자리마다 숫자를 2배 증가시킵니다. 예를 들어 `1234`에서 `3`과 `1`에 각각 2를 곱합니다.\n"
|
|
"\n"
|
|
"* 두배로 만든 숫자가 2자리라면 각 자리 숫자를 더합니다. 예를 들어, `7`은 두배로 만들면 `14`이므로 `5`가 됩니다.\n"
|
|
"\n"
|
|
"* 모든 자리의 숫자를 더합니다.\n"
|
|
"\n"
|
|
"* 합계의 끝자리가 `0`인 경우 유효한 신용카드 번호입니다."
|
|
|
|
#: src/exercises/day-2/luhn.md:19
|
|
msgid ""
|
|
"Copy the following code to <https://play.rust-lang.org/> and implement the\n"
|
|
"function:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사하고 함수를 구현해 보시기 바랍니다:"
|
|
|
|
#: src/exercises/day-2/luhn.md:23
|
|
msgid ""
|
|
"```rust\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"pub fn luhn(cc_number: &str) -> bool {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_non_digit_cc_number() {\n"
|
|
" assert!(!luhn(\"foo\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_empty_cc_number() {\n"
|
|
" assert!(!luhn(\"\"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_single_digit_cc_number() {\n"
|
|
" assert!(!luhn(\"0\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_two_digit_cc_number() {\n"
|
|
" assert!(luhn(\" 0 0 \"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_valid_cc_number() {\n"
|
|
" assert!(luhn(\"4263 9826 4026 9299\"));\n"
|
|
" assert!(luhn(\"4539 3195 0343 6467\"));\n"
|
|
" assert!(luhn(\"7992 7398 713\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_invalid_cc_number() {\n"
|
|
" assert!(!luhn(\"4223 9826 4026 9299\"));\n"
|
|
" assert!(!luhn(\"4539 3195 0343 6476\"));\n"
|
|
" assert!(!luhn(\"8273 1232 7352 0569\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[allow(dead_code)]\n"
|
|
"fn main() {}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/strings-iterators.md:1
|
|
msgid "# Strings and Iterators"
|
|
msgstr "# 문자열과 반복자"
|
|
|
|
#: src/exercises/day-2/strings-iterators.md:3
|
|
msgid ""
|
|
"In this exercise, you are implementing a routing component of a web server. The\n"
|
|
"server is configured with a number of _path prefixes_ which are matched against\n"
|
|
"_request paths_. The path prefixes can contain a wildcard character which\n"
|
|
"matches a full segment. See the unit tests below."
|
|
msgstr "이번 훈련은 웹 서버의 라우팅 컴포넌트를 구현합니다. 서버는 _요청 경로(request path)_ 를 처리할 수 있는 여러 개의 _경로 접두사(path prefix)_ 로 구성됩니다. 경로 접두사는 와일드카드문자를 포함할 수 있습니다. 아래 단위 테스트 코드를 참조하세요."
|
|
|
|
#: src/exercises/day-2/strings-iterators.md:8
|
|
msgid ""
|
|
"Copy the following code to <https://play.rust-lang.org/> and make the tests\n"
|
|
"pass. Try avoiding allocating a `Vec` for your intermediate results:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사하고 테스트를 통과해 보시기 바랍니다. 중간 결과값을 `Vec`에 할당하지 않도록 주의 하시기 바랍니다:"
|
|
|
|
#: src/exercises/day-2/strings-iterators.md:12
|
|
msgid ""
|
|
"```rust\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n"
|
|
" unimplemented!()\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_matches_without_wildcard() {\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc-123\"));\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/books\"));\n"
|
|
"\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/publishers\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_matches_with_wildcard() {\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/books\"\n"
|
|
" ));\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/bar/books\"\n"
|
|
" ));\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/books/book1\"\n"
|
|
" ));\n"
|
|
"\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/publishers\"));\n"
|
|
" assert!(!prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/booksByAuthor\"\n"
|
|
" ));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/welcome-day-3.md:1
|
|
msgid "# Welcome to Day 3"
|
|
msgstr "# 3일차 개요"
|
|
|
|
#: src/welcome-day-3.md:3
|
|
msgid "Today, we will cover some more advanced topics of Rust:"
|
|
msgstr "오늘은 몇 가지 고급 주제를 다룹니다:"
|
|
|
|
#: src/welcome-day-3.md:5
|
|
msgid ""
|
|
"* Traits: deriving traits, default methods, and important standard library\n"
|
|
" traits.\n"
|
|
"\n"
|
|
"* Generics: generic data types, generic methods, monomorphization, and trait\n"
|
|
" objects.\n"
|
|
"\n"
|
|
"* Error handling: panics, `Result`, and the try operator `?`.\n"
|
|
"\n"
|
|
"* Testing: unit tests, documentation tests, and integration tests.\n"
|
|
"\n"
|
|
"* Unsafe Rust: raw pointers, static variables, unsafe functions, and extern\n"
|
|
" functions."
|
|
msgstr ""
|
|
"* 트레잇: 트레잇 상속(derive), 디폴트 메서드, 표준 라이브러에 있는 중요한 트레잇들.\n"
|
|
"\n"
|
|
"* 제네릭: 제네릭 데이터 타입, 제네릭 메서드, 단형화(monomorphization), 트레잇 객체.\n"
|
|
"\n"
|
|
"* 오류처리(에러 핸들링): 패닉, `Result`, `?` 연산자.\n"
|
|
"\n"
|
|
"* 테스트: 단위 테스트, 문서 테스트 및 통합 테스트.\n"
|
|
"\n"
|
|
"* 안전하지 않은 러스트: 원시(raw) 포인터, 정적 변수, 안전하지 않은 함수, 외부 함수."
|
|
|
|
#: src/traits.md:1
|
|
msgid "# Traits"
|
|
msgstr "# 트레잇(Trait)"
|
|
|
|
#: src/traits.md:3
|
|
msgid "Rust lets you abstract over types with traits. They're similar to interfaces:"
|
|
msgstr "트레잇은 타입을 추상화 하는데 사용됩니다. 인터페이스와 비슷합니다:"
|
|
|
|
#: src/traits.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"trait Greet {\n"
|
|
" fn say_hello(&self);\n"
|
|
"}\n"
|
|
"\n"
|
|
"struct Dog {\n"
|
|
" name: String,\n"
|
|
"}\n"
|
|
"\n"
|
|
"struct Cat; // No name, cats won't respond to it anyway.\n"
|
|
"\n"
|
|
"impl Greet for Dog {\n"
|
|
" fn say_hello(&self) {\n"
|
|
" println!(\"Wuf, my name is {}!\", self.name);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Greet for Cat {\n"
|
|
" fn say_hello(&self) {\n"
|
|
" println!(\"Miau!\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let pets: Vec<Box<dyn Greet>> = vec![\n"
|
|
" Box::new(Dog { name: String::from(\"Fido\") }),\n"
|
|
" Box::new(Cat),\n"
|
|
" ];\n"
|
|
" for pet in pets {\n"
|
|
" pet.say_hello();\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits.md:41
|
|
msgid ""
|
|
"* Traits may specify pre-implemented (default) methods and methods that users are required to implement themselves. Methods with default implementations can rely on required methods.\n"
|
|
"* Types that implement a given trait may be of different sizes. This makes it impossible to have things like `Vec<Greet>` in the example above.\n"
|
|
"* `dyn Greet` is a way to tell the compiler about a dynamically sized type that implements `Greet`.\n"
|
|
"* In the example, `pets` holds Fat Pointers to objects that implement `Greet`. The Fat Pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for the `Greet` implementation of that particular object.\n"
|
|
"* Compare these outputs in the above example:\n"
|
|
" ```rust,ignore\n"
|
|
" println!(\"{} {}\", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());\n"
|
|
" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());\n"
|
|
" println!(\"{}\", std::mem::size_of::<&dyn Greet>());\n"
|
|
" println!(\"{}\", std::mem::size_of::<Box<dyn Greet>>());\n"
|
|
" ```"
|
|
msgstr ""
|
|
"* 트레잇은 사용자가 직접 구현해야 하는 메서드와 사전 구현된(디폴트) 메서드를 지정할 수 있습니다. 기본 구현된 메서드는 (다른)필수 메서드를 의존할 수 있습니다.\n"
|
|
"* 여러 타입이 같은 트레잇을 구현하더라도 그 크기는 서로 다를 수 있습니다. 그래서 `Vec<Greeet>`같은 것은 불가능합니다. 왜냐하면 `Greet`트레잇을 구현하는 타입들 중 어떤 크기의 타입이 실제로 사용될 지, 컴파일 시에 알 방법이 없는 반면, `Vec<>`는 타입의 크기를 컴파일 시에 알고자 하기 때문입니다.\n"
|
|
"* `dyn Greet`는 `Greet`을 구현하는, 가변 크기의 타입을 의미합니다.\n"
|
|
"* 예제에서 `pets`는 `Greet`을 구현하는 객체들의 Fat pointer를 담고 있습니다. Fat pointer는 실제 객체에 대한 포인터와 그 객체가 `Greet`을 구현하고 있는 가상 함수 테이블에 대한 포인터를 가집니다.\n"
|
|
"* 아래 코드의 결과와 비교해보세요:\n"
|
|
" ```rust,ignore\n"
|
|
" println!(\"{} {}\", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());\n"
|
|
" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());\n"
|
|
" println!(\"{}\", std::mem::size_of::<&dyn Greet>());\n"
|
|
" println!(\"{}\", std::mem::size_of::<Box<dyn Greet>>());\n"
|
|
" ```"
|
|
|
|
#: src/traits/deriving-traits.md:1
|
|
msgid "# Deriving Traits"
|
|
msgstr "# 트레잇 상속하기(Deriving Traits)"
|
|
|
|
#: src/traits/deriving-traits.md:3
|
|
msgid "You can let the compiler derive a number of traits:"
|
|
msgstr "컴파일러가 여러가지 트레잇을 상속(derive)하도록 할 수 있습니다. 이 경우 컴파일러가 트레잇을 자동으로 구현합니다:"
|
|
|
|
#: src/traits/deriving-traits.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug, Clone, PartialEq, Eq, Default)]\n"
|
|
"struct Player {\n"
|
|
" name: String,\n"
|
|
" strength: u8,\n"
|
|
" hit_points: u8,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1 = Player::default();\n"
|
|
" let p2 = p1.clone();\n"
|
|
" println!(\"Is {:?}\\nequal to {:?}?\\nThe answer is {}!\", &p1, &p2,\n"
|
|
" if p1 == p2 { \"yes\" } else { \"no\" });\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/default-methods.md:1
|
|
msgid "# Default Methods"
|
|
msgstr "# 디폴트 메서드"
|
|
|
|
#: src/traits/default-methods.md:3
|
|
msgid "Traits can implement behavior in terms of other trait methods:"
|
|
msgstr "트레잇의 디폴트 메서드에서 다른(구현되지 않은) 메소드를 이용할 수 있습니다:"
|
|
|
|
#: src/traits/default-methods.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"trait Equals {\n"
|
|
" fn equal(&self, other: &Self) -> bool;\n"
|
|
" fn not_equal(&self, other: &Self) -> bool {\n"
|
|
" !self.equal(other)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Centimeter(i16);\n"
|
|
"\n"
|
|
"impl Equals for Centimeter {\n"
|
|
" fn equal(&self, other: &Centimeter) -> bool {\n"
|
|
" self.0 == other.0\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let a = Centimeter(10);\n"
|
|
" let b = Centimeter(20);\n"
|
|
" println!(\"{a:?} equals {b:?}: {}\", a.equal(&b));\n"
|
|
" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equal(&b));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/important-traits.md:1
|
|
msgid "# Important Traits"
|
|
msgstr "# 중요한 트레잇들"
|
|
|
|
#: src/traits/important-traits.md:3
|
|
msgid "We will now look at some of the most common traits of the Rust standard library:"
|
|
msgstr "러스트 표준 라이브러리에는 다음과 같은 범용 트레잇들이 정의되어 있습니다:"
|
|
|
|
#: src/traits/important-traits.md:5
|
|
msgid ""
|
|
"* [`Iterator`][1] and [`IntoIterator`][2] used in `for` loops,\n"
|
|
"* [`From`][3] and [`Into`][4] used to convert values,\n"
|
|
"* [`Read`][5] and [`Write`][6] used for IO,\n"
|
|
"* [`Add`][7], [`Mul`][8], ... used for operator overloading, and\n"
|
|
"* [`Drop`][9] used for defining destructors.\n"
|
|
"* [`Default`][10] used to construct a default instance of a type."
|
|
msgstr ""
|
|
"* [`Iterator`][1]와 [`IntoIterator`][2] 트레잇은 `for` 루프에서 사용됩니다.\n"
|
|
"* [`From`][3]과 [`Into`][4] 트레잇은 값을 변환할 때 사용됩니다.\n"
|
|
"* [`Read`][5]와 [`Write`][6] 트레잇은 I/O에 사용됩니다.\n"
|
|
"* [`Add`][7], [`Mul`][8] 등의 트레잇들은 연산자 오버로딩(overloading)에 사용됩니다.\n"
|
|
"* [`Drop`][9] 트레잇은 소멸자 정의에 사용됩니다.\n"
|
|
"* [`Default`][10] 트레잇은 어떤 타입의 기본값 인스턴스를 만들때 사용됩니다."
|
|
|
|
#: src/traits/iterator.md:1
|
|
msgid "# Iterators"
|
|
msgstr "# Iterators"
|
|
|
|
#: src/traits/iterator.md:3
|
|
msgid "You can implement the [`Iterator`][1] trait on your own types:"
|
|
msgstr "`Iterator`트레잇을 여러분이 정의한 타입에서 구현해 보겠습니다:"
|
|
|
|
#: src/traits/iterator.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Fibonacci {\n"
|
|
" curr: u32,\n"
|
|
" next: u32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Iterator for Fibonacci {\n"
|
|
" type Item = u32;\n"
|
|
"\n"
|
|
" fn next(&mut self) -> Option<Self::Item> {\n"
|
|
" let new_next = self.curr + self.next;\n"
|
|
" self.curr = self.next;\n"
|
|
" self.next = new_next;\n"
|
|
" Some(self.curr)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let fib = Fibonacci { curr: 0, next: 1 };\n"
|
|
" for (i, n) in fib.enumerate().take(5) {\n"
|
|
" println!(\"fib({i}): {n}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/iterator.md:32
|
|
msgid ""
|
|
"* `IntoIterator` is the trait that makes for loops work. It is implemented by collection types such as\n"
|
|
" `Vec<T>` and references to them such as `&Vec<T>` and `&[T]`. Ranges also implement it.\n"
|
|
"* The `Iterator` trait implements many common functional programming operations over collections \n"
|
|
" (e.g. `map`, `filter`, `reduce`, etc). This is the trait where you can find all the documentation\n"
|
|
" about them. In Rust these functions should produce the code as efficient as equivalent imperative\n"
|
|
" implementations.\n"
|
|
" "
|
|
msgstr ""
|
|
"* `IntoIterator`는 루프가 작동하도록 만드는 트레잇입니다. `Vec<T>`와 같은 컬렉션 타입과 그에 대한 참조 타입(`&Vec<T>`, `&[T]`)들 모두 이 트레잇을 구현합니다. Range 역시 이를 구현합니다.\n"
|
|
"* `Iterator` 트레잇은 컬렉션에 대해 다양한 함수형 프로그래밍 연산 (`map`, `filter`, `reduce` 등)을 구현합니다. 이 연산들에 대한 자세한 설명은 `Iterator` 트레잇의 API 레퍼런스에서 찾을 수 있습니다. 러스트에서 이러한 함수형 연산들은 절차형으로 구현된 코드와 동일한 성능을 보여줍니다.\n"
|
|
" "
|
|
|
|
#: src/traits/from-iterator.md:1
|
|
msgid "# FromIterator"
|
|
msgstr "# FromIterator"
|
|
|
|
#: src/traits/from-iterator.md:3
|
|
msgid "[`FromIterator`][1] lets you build a collection from an [`Iterator`][2]."
|
|
msgstr "어떤 컬렉션이 [`FromIterator`][1]를 구현하고 있다면 [`Iterator`][2]로부터 그 컬렉션을 만들 수 있습니다."
|
|
|
|
#: src/traits/from-iterator.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let primes = vec![2, 3, 5, 7];\n"
|
|
" let prime_squares = primes\n"
|
|
" .into_iter()\n"
|
|
" .map(|prime| prime * prime)\n"
|
|
" .collect::<Vec<_>>();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/from-iterator.md:17
|
|
msgid ""
|
|
"`Iterator` implements\n"
|
|
"`fn collect<B>(self) -> B\n"
|
|
"where\n"
|
|
" B: FromIterator<Self::Item>,\n"
|
|
" Self: Sized`"
|
|
msgstr ""
|
|
"`Iterator`에는 다음 함수가 정의되어 있습니다:\n"
|
|
"`fn collect<B>(self) -> B\n"
|
|
"where\n"
|
|
" B: FromIterator<Self::Item>,\n"
|
|
" Self: Sized`"
|
|
|
|
#: src/traits/from-iterator.md:23
|
|
msgid ""
|
|
"There are also implementations which let you do cool things like convert an\n"
|
|
"`Iterator<Item = Result<V, E>>` into a `Result<Vec<V>, E>`."
|
|
msgstr "`Iterator<Item = Result<V, E>>`을 `Result<Vec<V>, E>`로 변환할 수 있는 멋진 기능들도 구현되어 있습니다."
|
|
|
|
#: src/traits/from-into.md:1
|
|
msgid "# `From` and `Into`"
|
|
msgstr "# `From`과 `Into`"
|
|
|
|
#: src/traits/from-into.md:3
|
|
msgid "Types implement [`From`][1] and [`Into`][2] to facilitate type conversions:"
|
|
msgstr "타입은 용이한 형변환을 위해 [`From`][1]과 [`Into`][2]를 구현합니다:"
|
|
|
|
#: src/traits/from-into.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s = String::from(\"hello\");\n"
|
|
" let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);\n"
|
|
" let one = i16::from(true);\n"
|
|
" let bigger = i32::from(123i16);\n"
|
|
" println!(\"{s}, {addr}, {one}, {bigger}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/from-into.md:15
|
|
msgid "[`Into`][2] is automatically implemented when [`From`][1] is implemented:"
|
|
msgstr "[`From`][1]이 구현되면 [`Into`][2] 역시 자동으로 구현됩니다:"
|
|
|
|
#: src/traits/from-into.md:17
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let s: String = \"hello\".into();\n"
|
|
" let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();\n"
|
|
" let one: i16 = true.into();\n"
|
|
" let bigger: i32 = 123i16.into();\n"
|
|
" println!(\"{s}, {addr}, {one}, {bigger}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/from-into.md:29
|
|
msgid ""
|
|
"* That's why it is common to only implement `From`, as your type will get `Into` implementation too.\n"
|
|
"* When declaring a function argument input type like \"anything that can be converted into a `String`\", the rule is opposite, you should use `Into`.\n"
|
|
" Your function will accept types that implement `From` and those that _only_ implement `Into`.\n"
|
|
" "
|
|
msgstr ""
|
|
"* 그렇기 때문에 사용자 정의 타입의 경우에도 `From` 만 구현하는 것이 일반적입니다.\n"
|
|
"* \"`String`으로 변환할 수 있는 모든 것\"과 같은 함수의 인수 타입을 선언할 때에는 `Into`를 사용해야 함을 조심하세요. 그래야만, 함수는 `From`을 구현한 타입과 `Into` _만_ 구현한 타입 모두를 인자로 받을 수 있습니다.\n"
|
|
" "
|
|
|
|
#: src/traits/read-write.md:1
|
|
msgid "# `Read` and `Write`"
|
|
msgstr "# `Read`와 `Write`"
|
|
|
|
#: src/traits/read-write.md:3
|
|
msgid "Using [`Read`][1] and [`BufRead`][2], you can abstract over `u8` sources:"
|
|
msgstr "[`Read`][1]와 [`BufRead`][2]를 사용하면 `u8` 타입의 데이터 스트림을 읽을 수 있습니다:"
|
|
|
|
#: src/traits/read-write.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::io::{BufRead, BufReader, Read, Result};\n"
|
|
"\n"
|
|
"fn count_lines<R: Read>(reader: R) -> usize {\n"
|
|
" let buf_reader = BufReader::new(reader);\n"
|
|
" buf_reader.lines().count()\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() -> Result<()> {\n"
|
|
" let slice: &[u8] = b\"foo\\nbar\\nbaz\\n\";\n"
|
|
" println!(\"lines in slice: {}\", count_lines(slice));\n"
|
|
"\n"
|
|
" let file = std::fs::File::open(std::env::current_exe()?)?;\n"
|
|
" println!(\"lines in file: {}\", count_lines(file));\n"
|
|
" Ok(())\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/read-write.md:23
|
|
msgid "Similarly, [`Write`][3] lets you abstract over `u8` sinks:"
|
|
msgstr "이와 비슷하게, `Write`를 사옹하면 `u8` 타입의 데이터를 쓸 수 있습니다:"
|
|
|
|
#: src/traits/read-write.md:25
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::io::{Result, Write};\n"
|
|
"\n"
|
|
"fn log<W: Write>(writer: &mut W, msg: &str) -> Result<()> {\n"
|
|
" writer.write_all(msg.as_bytes())?;\n"
|
|
" writer.write_all(\"\\n\".as_bytes())\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() -> Result<()> {\n"
|
|
" let mut buffer = Vec::new();\n"
|
|
" log(&mut buffer, \"Hello\")?;\n"
|
|
" log(&mut buffer, \"World\")?;\n"
|
|
" println!(\"Logged: {:?}\", buffer);\n"
|
|
" Ok(())\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/operators.md:1
|
|
msgid "# `Add`, `Mul`, ..."
|
|
msgstr "# `Add`, `Mul`, ..."
|
|
|
|
#: src/traits/operators.md:3
|
|
msgid "Operator overloading is implemented via traits in [`std::ops`][1]:"
|
|
msgstr "연산자 오버로드는 `std::ops`에 있는 다양한 트레잇들을 통해 구현됩니다:"
|
|
|
|
#: src/traits/operators.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug, Copy, Clone)]\n"
|
|
"struct Point { x: i32, y: i32 }\n"
|
|
"\n"
|
|
"impl std::ops::Add for Point {\n"
|
|
" type Output = Self;\n"
|
|
"\n"
|
|
" fn add(self, other: Self) -> Self {\n"
|
|
" Self {x: self.x + other.x, y: self.y + other.y}\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p1 = Point { x: 10, y: 20 };\n"
|
|
" let p2 = Point { x: 100, y: 200 };\n"
|
|
" println!(\"{:?} + {:?} = {:?}\", p1, p2, p1 + p2);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/operators.md:26 src/traits/drop.md:34
|
|
msgid "Discussion points:"
|
|
msgstr "논의점:"
|
|
|
|
#: src/traits/operators.md:28
|
|
msgid ""
|
|
"* You could implement `Add` for `&Point`. In which situations is that useful? \n"
|
|
" * Answer: `Add:add` consumes `self`. If type `T` for which you are\n"
|
|
" overloading the operator is not `Copy`, you should consider overloading\n"
|
|
" the operator for `&T` as well. This avoids unnecessary cloning on the\n"
|
|
" call site.\n"
|
|
"* Why is `Output` an associated type? Could it be made a type parameter?\n"
|
|
" * Short answer: Type parameters are controlled by the caller, but\n"
|
|
" associated types (like `Output`) are controlled by the implementor of a\n"
|
|
" trait."
|
|
msgstr ""
|
|
"* `&Point`가 `Add`를 구현하도록 할 수도 있습니다. 이게 어떤 경우에 유용할까요?\n"
|
|
" * 답: `Add:add`는 `self`를 소모합니다. 만약 타입 `T`가 `Copy`트레잇을 구현하고 있지 않다면 `&T`에 대해서도 연산자 오버로딩을 고려해야 합니다. 이렇게 하면 호출부에서 불필요한 복사를 피할 수 있습니다.\n"
|
|
"* 왜 `Output`이 연관된 타입인가요? 타입 파라메터로 만들 수 있을까요?\n"
|
|
" * 답: 타입 파라메터를 호출하는 쪽에서 결정합니다. 반면 연관된 타입(`Output`같은) 은 트레잇을 구현하는 쪽에서 제어 가능합니다."
|
|
|
|
#: src/traits/drop.md:1
|
|
msgid "# The `Drop` Trait"
|
|
msgstr "# `Drop` 트레잇"
|
|
|
|
#: src/traits/drop.md:3
|
|
msgid "Values which implement [`Drop`][1] can specify code to run when they go out of scope:"
|
|
msgstr "[`Drop`][1]트레잇을 구현하면, 그 값이 스코프 밖으로 나갈 때 실행될 코드를 작성할 수 있습니다:"
|
|
|
|
#: src/traits/drop.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"struct Droppable {\n"
|
|
" name: &'static str,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Drop for Droppable {\n"
|
|
" fn drop(&mut self) {\n"
|
|
" println!(\"Dropping {}\", self.name);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let a = Droppable { name: \"a\" };\n"
|
|
" {\n"
|
|
" let b = Droppable { name: \"b\" };\n"
|
|
" {\n"
|
|
" let c = Droppable { name: \"c\" };\n"
|
|
" let d = Droppable { name: \"d\" };\n"
|
|
" println!(\"Exiting block B\");\n"
|
|
" }\n"
|
|
" println!(\"Exiting block A\");\n"
|
|
" }\n"
|
|
" drop(a);\n"
|
|
" println!(\"Exiting main\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/drop.md:36
|
|
msgid ""
|
|
"* Why doesn't `Drop::drop` take `self`?\n"
|
|
" * Short-answer: If it did, `std::mem::drop` would be called at the end of\n"
|
|
" the block, resulting in another call to `Drop::drop`, and a stack\n"
|
|
" overflow!\n"
|
|
"* Try replacing `drop(a)` with `a.drop()`."
|
|
msgstr ""
|
|
"* `Drop::drop`은 왜 `self`를 인자로 받지 않습니까?\n"
|
|
" * 짧은 대답: 만약 그렇게 된다면 `std::mem::drop`이 블록의 끝에서 호출되고, 다시 `Drop::drop`을 호출하게되 스택 오버플로가 발생합니다.\n"
|
|
"* `drop(a)`를 `a.drop()`로 변경해 보시기 바랍니다."
|
|
|
|
#: src/traits/default.md:1
|
|
msgid "# The `Default` Trait"
|
|
msgstr "# `Default` 트레잇"
|
|
|
|
#: src/traits/default.md:3
|
|
msgid "[`Default`][1] trait provides a default implementation of a trait."
|
|
msgstr "[`Default`][1] 트레잇은 기본 구현을 제공합니다."
|
|
|
|
#: src/traits/default.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug, Default)]\n"
|
|
"struct Derived {\n"
|
|
" x: u32,\n"
|
|
" y: String,\n"
|
|
" z: Implemented,\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Implemented(String);\n"
|
|
"\n"
|
|
"impl Default for Implemented {\n"
|
|
" fn default() -> Self {\n"
|
|
" Self(\"John Smith\".into())\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let default_struct: Derived = Default::default();\n"
|
|
" println!(\"{default_struct:#?}\");\n"
|
|
"\n"
|
|
" let almost_default_struct = Derived {\n"
|
|
" y: \"Y is set!\".into(),\n"
|
|
" ..Default::default()\n"
|
|
" };\n"
|
|
" println!(\"{almost_default_struct:#?}\");\n"
|
|
"\n"
|
|
" let nothing: Option<Derived> = None;\n"
|
|
" println!(\"{:#?}\", nothing.unwrap_or_default());\n"
|
|
"}\n"
|
|
"\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/traits/default.md:40
|
|
msgid ""
|
|
" * It can be implemented directly or it can be derived via `#[derive(Default)]`.\n"
|
|
" * Derived implementation will produce an instance where all fields are set to their default values.\n"
|
|
" * This means all types in the struct must implement `Default` too.\n"
|
|
" * Standard Rust types often implement `Default` with reasonable values (e.g. `0`, `\"\"`, etc).\n"
|
|
" * The partial struct copy works nicely with default.\n"
|
|
" * Rust standard library is aware that types can implement `Default` and provides convenience methods that use it."
|
|
msgstr ""
|
|
" * 트레잇을 직접 구현하거나 `#[derive(Default)]`를 붙여서 컴파일러에게 구현을 맡길 수 있습니다.\n"
|
|
" * 컴파일러가 제공하는 자동 구현의 경우 모든 필드에 대해 기본 값을 설정한 새 인스턴스를 반환합니다.\n"
|
|
" * 구조체의 각 필드 타입들이 모두 `Default`를 구현해야 함을 의미합니다.\n"
|
|
" * 러스트 표준 타입들은 대부분 `Default`를 구현하고 있으며, 기본 값은 `0`이나 `\"\"`처럼 예상 가능한 값들입니다.\n"
|
|
" * 구조체 부분 복사를 할때 `default`를 편리하게 사용할 수 있습니다.\n"
|
|
" * 러스트 표준 라이브러리는 `Default` 트레잇을 구현한 타입을 위한 편의 메서드를 제공하기도 합니다."
|
|
|
|
#: src/generics.md:1
|
|
msgid "# Generics"
|
|
msgstr "# 제네릭"
|
|
|
|
#: src/generics.md:3
|
|
msgid ""
|
|
"Rust support generics, which lets you abstract an algorithm (such as sorting)\n"
|
|
"over the types used in the algorithm."
|
|
msgstr "러스트는 제네릭을 지원합니다. 이를 이용하면 알고리즘(정렬과 같은)을 추상화 하여 특정 타입에 의존하지 않도록 할 수 있습니다."
|
|
|
|
#: src/generics/data-types.md:1
|
|
msgid "# Generic Data Types"
|
|
msgstr "# 제네릭 데이터 타입"
|
|
|
|
#: src/generics/data-types.md:3
|
|
msgid "You can use generics to abstract over the concrete field type:"
|
|
msgstr "제네릭을 사용하여 필드의 타입을 추상화 할 수 있습니다:"
|
|
|
|
#: src/generics/data-types.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Point<T> {\n"
|
|
" x: T,\n"
|
|
" y: T,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let integer = Point { x: 5, y: 10 };\n"
|
|
" let float = Point { x: 1.0, y: 4.0 };\n"
|
|
" println!(\"{integer:?} and {float:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/methods.md:1
|
|
msgid "# Generic Methods"
|
|
msgstr "# 제네릭 메서드"
|
|
|
|
#: src/generics/methods.md:3
|
|
msgid "You can declare a generic type on your `impl` block:"
|
|
msgstr "`impl` 블록에서도 제네릭 타입을 선언할 수 있습니다:"
|
|
|
|
#: src/generics/methods.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct Point<T>(T, T);\n"
|
|
"\n"
|
|
"impl<T> Point<T> {\n"
|
|
" fn x(&self) -> &T {\n"
|
|
" &self.0 // + 10\n"
|
|
" }\n"
|
|
"\n"
|
|
" // fn set_x(&mut self, x: T)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let p = Point(5, 10);\n"
|
|
" println!(\"p.x = {}\", p.x());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/methods.md:25
|
|
msgid ""
|
|
"* *Q:* Why `T` is specified twice in `impl<T> Point<T> {}`? Isn't that redundant?\n"
|
|
" * This is because it is a generic implementation section for generic type. They are independently generic.\n"
|
|
" * It means these methods are defined for any `T`.\n"
|
|
" * It is possible to write `impl Point<u32> { .. }`. \n"
|
|
" * `Point` is still generic and you can use `Point<f64>`, but methods in this block will only be available for `Point<u32>`."
|
|
msgstr ""
|
|
"* *질문:* `impl<T> Point<T> {}`에서 `T`가 왜 두 번 사용됩니까?\n"
|
|
" * 제네릭 타입에 대한 제네릭 구현 이기 때문입니다. 이 두 제네릭은 서로 독립적입니다.\n"
|
|
" * 이는 임의의 모든 `T`에 대해서 이 메소드들이 정의된다는 것을 의미합니다.\n"
|
|
" * `impl Point<u32> { .. }`와 같이 작성하는 것도 가능합니다.\n"
|
|
" * `Point`는 여전히 제네릭이며 `Point<f64>`를 사용할 수도 있지만 이 블록의 메서드는 `Point<u32>`만 쓸 수 있습니다."
|
|
|
|
#: src/generics/trait-bounds.md:1
|
|
msgid "# Trait Bounds"
|
|
msgstr "# 제네릭 타입 제한(트레잇 경계, Trait Bounds)"
|
|
|
|
#: src/generics/trait-bounds.md:3
|
|
msgid ""
|
|
"When working with generics, you often want to require the types to implement\n"
|
|
"some trait, so that you can call this trait's methods."
|
|
msgstr "제네릭을 이용하다 보면 타입이 어떤 트레잇을 구현하고 있어야 하는 경우가 있습니다. 그래야 그 트레잇의 메서드를 호출할 수 있기 때문입니다."
|
|
|
|
#: src/generics/trait-bounds.md:6
|
|
msgid "You can do this with `T: Trait` or `impl Trait`:"
|
|
msgstr "`T: Trait` 혹은 `impl Trait`를 사용하면 됩니다:"
|
|
|
|
#: src/generics/trait-bounds.md:8
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn duplicate<T: Clone>(a: T) -> (T, T) {\n"
|
|
" (a.clone(), a.clone())\n"
|
|
"}\n"
|
|
"\n"
|
|
"// Syntactic sugar for:\n"
|
|
"// fn add_42_millions<T: Into<i32>>(x: T) -> i32 {\n"
|
|
"fn add_42_millions(x: impl Into<i32>) -> i32 {\n"
|
|
" x.into() + 42_000_000\n"
|
|
"}\n"
|
|
"\n"
|
|
"// struct NotClonable;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let foo = String::from(\"foo\");\n"
|
|
" let pair = duplicate(foo);\n"
|
|
" println!(\"{pair:?}\");\n"
|
|
"\n"
|
|
" let many = add_42_millions(42_i8);\n"
|
|
" println!(\"{many}\");\n"
|
|
" let many_more = add_42_millions(10_000_000);\n"
|
|
" println!(\"{many_more}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/trait-bounds.md:35
|
|
msgid "Show a `where` clause, students will encounter it when reading code."
|
|
msgstr "`where` 문법을 사용할 수도 있습니다. 수강생들도 코드를 읽다가 그 문법을 마주할 수 있습니다."
|
|
|
|
#: src/generics/trait-bounds.md:37
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"fn duplicate<T>(a: T) -> (T, T)\n"
|
|
"where\n"
|
|
" T: Clone,\n"
|
|
"{\n"
|
|
" (a.clone(), a.clone())\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,ignore\n"
|
|
"fn duplicate<T>(a: T) -> (T, T)\n"
|
|
"where\n"
|
|
" T: Clone,\n"
|
|
"{\n"
|
|
" (a.clone(), a.clone())\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/generics/trait-bounds.md:46
|
|
msgid ""
|
|
"* It declutters the function signature if you have many parameters.\n"
|
|
"* It has additional features making it more powerful.\n"
|
|
" * If someone asks, the extra feature is that the type on the left of \":\" can be arbitrary, like `Option<T>`.\n"
|
|
" "
|
|
msgstr ""
|
|
"* 이를 이용하면 타입 파라메터가 많은 경우 함수 시그니처를 간결하게 정리하는 데 도움이 됩니다.\n"
|
|
"* 좀 더 강력한 추가 기능도 제공합니다.\n"
|
|
" * `:` 왼쪽에 임의의 타입(예를 들어 `Option<T>`)을 사용할 수 있습니다.\n"
|
|
" "
|
|
|
|
#: src/generics/impl-trait.md:1
|
|
msgid "# `impl Trait`"
|
|
msgstr "# 트레잇 구현하기(`impl Trait`)"
|
|
|
|
#: src/generics/impl-trait.md:3
|
|
msgid ""
|
|
"Similar to trait bounds, an `impl Trait` syntax can be used in function\n"
|
|
"arguments and return values:"
|
|
msgstr "트레잇 바운드와 유사하게 `impl Trait` 문법은 함수의 인자와 반환값에도 적용 가능합니다:"
|
|
|
|
#: src/generics/impl-trait.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::fmt::Display;\n"
|
|
"\n"
|
|
"fn get_x(name: impl Display) -> impl Display {\n"
|
|
" format!(\"Hello {name}\")\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let x = get_x(\"foo\");\n"
|
|
" println!(\"{x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/impl-trait.md:19
|
|
msgid ""
|
|
"* `impl Trait` cannot be used with the `::<>` turbo fish syntax.\n"
|
|
"* `impl Trait` allows you to work with types which you cannot name."
|
|
msgstr ""
|
|
"* `impl Trait`를 터보피쉬문법(`::<>`)과 함께 사용할 수는 없습니다.\n"
|
|
"* `impl Trait`를 이용하면 이름이 없는 타입을 다룰 수 있습니다."
|
|
|
|
#: src/generics/impl-trait.md:24
|
|
msgid "The meaning of `impl Trait` is a bit different in the different positions."
|
|
msgstr "`impl Trait`는 어디에 사용되었느냐에 따라 의미가 조금씩 다릅니다."
|
|
|
|
#: src/generics/impl-trait.md:26
|
|
msgid ""
|
|
"* For a parameter, `impl Trait` is like an anonymous generic parameter with a trait bound.\n"
|
|
"* For a return type, it means that the return type is some concrete type that implements the trait,\n"
|
|
" without naming the type. This can be useful when you don't want to expose the concrete type in a\n"
|
|
" public API."
|
|
msgstr ""
|
|
"* 함수 인자의 타입으로 사용되었을 경우에는 `impl Trait`는 트레잇 경계가 있는 익명의 제네릭 타입을 의미합니다.\n"
|
|
"* 리턴 타입으로 사용되었을 경우에는, 그 트레잇을 구현하는 구체적인 타입인데, 타입 이름을 프로그래머가 짓지 않았다는 것을 의미합니다. 이는 그 구체적인 타입 이름을 API로 공개하고 싶지 않은 경우에 유용합니다."
|
|
|
|
#: src/generics/impl-trait.md:31
|
|
msgid ""
|
|
"This example is great, because it uses `impl Display` twice. It helps to explain that\n"
|
|
"nothing here enforces that it is _the same_ `impl Display` type. If we used a single \n"
|
|
"`T: Display`, it would enforce the constraint that input `T` and return `T` type are the same type.\n"
|
|
"It would not work for this particular function, as the type we expect as input is likely not\n"
|
|
"what `format!` returns. If we wanted to do the same via `: Display` syntax, we'd need two\n"
|
|
"independent generic parameters."
|
|
msgstr "이 예시는 `impl Display`가 두번 사용 되었다는 점에서 훌륭합니다. 여기서 중요한 것은 이 두 `impl Display`가 실제로 같은 타입일 필요가 없다는 것입니다. 만약 `T: Display`로 트레잇 경계를 정하고 입력 파라메터와 리턴 값의 타입을 모두 `T`로 했다면, 이는 입력과 리턴값이 같은 타입임을 강제합니다. 이렇게 했다면 위의 예제는 동작하지 않았을 것입니다. 왜냐하면, 입력 값의 타입이 `format!`이 리턴하는 타입과 같지 않을 가능성이 높기 때문입니다. 만약 `: Display` 문법을 사용하고 싶다면 독립적인 제네릭 매개변수가 두 개가 필요합니다."
|
|
|
|
#: src/generics/closures.md:1
|
|
msgid "# Closures"
|
|
msgstr "# 클로저(Closure)"
|
|
|
|
#: src/generics/closures.md:3
|
|
msgid ""
|
|
"Closures or lambda expressions have types which cannot be named. However, they\n"
|
|
"implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html),\n"
|
|
"[`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), and\n"
|
|
"[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:"
|
|
msgstr "클로저 혹은 람다표현식은 익명타입입니다. 이들은 [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html),[`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) 라는 특별한 트레잇을 구현합니다:"
|
|
|
|
#: src/generics/closures.md:8
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {\n"
|
|
" println!(\"Calling function on {input}\");\n"
|
|
" func(input)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let add_3 = |x| x + 3;\n"
|
|
" let mul_5 = |x| x * 5;\n"
|
|
"\n"
|
|
" println!(\"add_3: {}\", apply_with_log(add_3, 10));\n"
|
|
" println!(\"mul_5: {}\", apply_with_log(mul_5, 20));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/closures.md:25
|
|
msgid "If you have an `FnOnce`, you may only call it once. It might consume captured values."
|
|
msgstr "`FnOnce`는 한번만 호출되며 캡처된 값을 소모합니다."
|
|
|
|
#: src/generics/closures.md:27
|
|
msgid "An `FnMut` might mutate captured values, so you can call it multiple times but not concurrently."
|
|
msgstr "`FnMut`는 캡처된 값을 변경할 수 있으므로 여러번 호출은 가능하지만 동시에 호출 할 수는 없습니다."
|
|
|
|
#: src/generics/closures.md:29
|
|
msgid ""
|
|
"An `Fn` neither consumes nor mutates captured values, or perhaps captures nothing at all, so it can\n"
|
|
"be called multiple times concurrently."
|
|
msgstr "`Fn`은 캡처된 값을 소모도 변경도 하지 않고, 혹은 어떤 것도 캡쳐하지 않았을 수도 있기 때문에 동시에 여러번 호출할 수 있습니다."
|
|
|
|
#: src/generics/closures.md:32
|
|
msgid ""
|
|
"`FnMut` is a subtype of `FnOnce`. `Fn` is a subtype of `FnMut` and `FnOnce`. I.e. you can use an\n"
|
|
"`FnMut` wherever an `FnOnce` is called for, and you can use an `Fn` wherever an `FnMut` or `FnOnce`\n"
|
|
"is called for."
|
|
msgstr "`FnMut` 는 `FnOnce`의 하위타입입니다. `Fn`은 `FnMut`과 `FnOnce`의 하위 타입입니다. 즉, `FnMut`는 `FnOnce`가 호출되는 곳이면 어디서나 사용 할 수 있고 `Fn`은 `FnMut`와 `FnOnce`가 호출되는 곳이면 어디든 사용할 수 있습니다."
|
|
|
|
#: src/generics/closures.md:36
|
|
msgid "`move` closures only implement `FnOnce`."
|
|
msgstr "클로저가 `move`와 함께 선언되었다면 그 클로저는 오직 `FnOnce`만 구현합니다."
|
|
|
|
#: src/generics/monomorphization.md:1
|
|
msgid "# Monomorphization"
|
|
msgstr "# 단형화(Monomorphization)"
|
|
|
|
#: src/generics/monomorphization.md:3
|
|
msgid "Generic code is turned into non-generic code based on the call sites:"
|
|
msgstr "제네릭 코드는 호출부에서 비 제네릭 코드로 전환됩니다:"
|
|
|
|
#: src/generics/monomorphization.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let integer = Some(5);\n"
|
|
" let float = Some(5.0);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/monomorphization.md:12
|
|
msgid "behaves as if you wrote"
|
|
msgstr "위 코드는 아래와 같이 동작합니다"
|
|
|
|
#: src/generics/monomorphization.md:14
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"enum Option_i32 {\n"
|
|
" Some(i32),\n"
|
|
" None,\n"
|
|
"}\n"
|
|
"\n"
|
|
"enum Option_f64 {\n"
|
|
" Some(f64),\n"
|
|
" None,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let integer = Option_i32::Some(5);\n"
|
|
" let float = Option_f64::Some(5.0);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/monomorphization.md:31
|
|
msgid ""
|
|
"This is a zero-cost abstraction: you get exactly the same result as if you had\n"
|
|
"hand-coded the data structures without the abstraction."
|
|
msgstr "이것이 바로 비용이 들지 않는 (zero-cost) 추상화 입니다: 러스트의 제네릭은 추상화를 거치지 않고 직접 구체적인 타입을 써서 코딩한 것과 정확히 동일한 결과를 보여줍니다."
|
|
|
|
#: src/generics/trait-objects.md:1
|
|
msgid "# Trait Objects"
|
|
msgstr "# 트레잇 객체"
|
|
|
|
#: src/generics/trait-objects.md:3
|
|
msgid "We've seen how a function can take arguments which implement a trait:"
|
|
msgstr "함수가 특정 트레잇을 구현하는 인자를 입력으로 받도록 하는 방법에 대해 설명했었습니다:"
|
|
|
|
#: src/generics/trait-objects.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::fmt::Display;\n"
|
|
"\n"
|
|
"fn print<T: Display>(x: T) {\n"
|
|
" println!(\"Your value: {x}\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" print(123);\n"
|
|
" print(\"Hello\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/trait-objects.md:18
|
|
msgid "However, how can we store a collection of mixed types which implement `Display`?"
|
|
msgstr "그런데, 아래와 같이 `Display`를 구현하는 서로 다른 타입들의 콜렉션을 저장하고 싶다면 어떻게 해야 할까요?"
|
|
|
|
#: src/generics/trait-objects.md:20
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"fn main() {\n"
|
|
" let xs = vec![123, \"Hello\"];\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/trait-objects.md:26
|
|
msgid "For this, we need _trait objects_:"
|
|
msgstr "이를 위해서는 \"트레잇 객체\"가 필요합니다:"
|
|
|
|
#: src/generics/trait-objects.md:28
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::fmt::Display;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let xs: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new(\"Hello\")];\n"
|
|
" for x in xs {\n"
|
|
" println!(\"x: {x}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/generics/trait-objects.md:39
|
|
msgid "Memory layout after allocating `xs`:"
|
|
msgstr "`xs`가 할당될때 메모리 레이아웃:"
|
|
|
|
#: src/generics/trait-objects.md:41
|
|
msgid ""
|
|
"```bob\n"
|
|
" Stack Heap\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": xs : : :\n"
|
|
": +-----------+-------+ : : +-----+-----+ :\n"
|
|
": | ptr | o---+---+-----+-->| o o | o o | :\n"
|
|
": | len | 2 | : : +-|-|-+-|-|-+ :\n"
|
|
": | capacity | 2 | : : | | | | +----+----+----+----+----+ :\n"
|
|
": +-----------+-------+ : : | | | '-->| H | e | l | l | o | :\n"
|
|
": : : | | | +----+----+----+----+----+ :\n"
|
|
"`- - - - - - - - - - - - - -' : | | | :\n"
|
|
" : | | | +-------------------------+ :\n"
|
|
" : | | '---->| \"<str as Display>::fmt\" | :\n"
|
|
" : | | +-------------------------+ :\n"
|
|
" : | | :\n"
|
|
" : | | +----+----+----+----+ :\n"
|
|
" : | '-->| 7b | 00 | 00 | 00 | :\n"
|
|
" : | +----+----+----+----+ :\n"
|
|
" : | :\n"
|
|
" : | +-------------------------+ :\n"
|
|
" : '---->| \"<i32 as Display>::fmt\" | :\n"
|
|
" : +-------------------------+ :\n"
|
|
" : :\n"
|
|
" : :\n"
|
|
" '- - - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
msgstr ""
|
|
"```bob\n"
|
|
" 스택 힙\n"
|
|
".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.\n"
|
|
": : : :\n"
|
|
": xs : : :\n"
|
|
": +-----------+-------+ : : +-----+-----+ :\n"
|
|
": | ptr | o---+---+-----+-->| o o | o o | :\n"
|
|
": | len | 2 | : : +-|-|-+-|-|-+ :\n"
|
|
": | capacity | 2 | : : | | | | +----+----+----+----+----+ :\n"
|
|
": +-----------+-------+ : : | | | '-->| H | e | l | l | o | :\n"
|
|
": : : | | | +----+----+----+----+----+ :\n"
|
|
"`- - - - - - - - - - - - - -' : | | | :\n"
|
|
" : | | | +-------------------------+ :\n"
|
|
" : | | '---->| \"<str as Display>::fmt\" | :\n"
|
|
" : | | +-------------------------+ :\n"
|
|
" : | | :\n"
|
|
" : | | +----+----+----+----+ :\n"
|
|
" : | '-->| 7b | 00 | 00 | 00 | :\n"
|
|
" : | +----+----+----+----+ :\n"
|
|
" : | :\n"
|
|
" : | +-------------------------+ :\n"
|
|
" : '---->| \"<i32 as Display>::fmt\" | :\n"
|
|
" : +-------------------------+ :\n"
|
|
" : :\n"
|
|
" : :\n"
|
|
" '- - - - - - - - - - - - - - - - - - - - - - - -'\n"
|
|
"```"
|
|
|
|
#: src/generics/trait-objects.md:69
|
|
msgid ""
|
|
"Similarly, you need a trait object if you want to return different types\n"
|
|
"implementing a trait:"
|
|
msgstr "마찬가지로, 같은 트레잇을 구현하는 서로 다른 타입의 값을 리턴할 때에도 트레잇 객체가 필요합니다:"
|
|
|
|
#: src/generics/trait-objects.md:72
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn numbers(n: i32) -> Box<dyn Iterator<Item=i32>> {\n"
|
|
" if n > 0 {\n"
|
|
" Box::new(0..n)\n"
|
|
" } else {\n"
|
|
" Box::new((n..0).rev())\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"{:?}\", numbers(-5).collect::<Vec<_>>());\n"
|
|
" println!(\"{:?}\", numbers(5).collect::<Vec<_>>());\n"
|
|
"}\n"
|
|
"\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/morning.md:1
|
|
msgid "# Day 3: Morning Exercises"
|
|
msgstr "# 3일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-3/morning.md:3
|
|
msgid "We will design a classical GUI library traits and trait objects."
|
|
msgstr "이번 연습문제에서는 트레잇와 트레잇 객체를 통해 고전적인 GUI 라이브러리를 설계할 것입니다."
|
|
|
|
#: src/exercises/day-3/simple-gui.md:1
|
|
msgid "# A Simple GUI Library"
|
|
msgstr "# 간단한 GUI 라이브러리"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:3
|
|
msgid ""
|
|
"Let us design a classical GUI library using our new knowledge of traits and\n"
|
|
"trait objects."
|
|
msgstr "이번 연습문제에서는 트레잇와 트레잇 객체에 대해 배운것을 활용하여 고전적인 GUI 라이브러리를 설계할 것입니다."
|
|
|
|
#: src/exercises/day-3/simple-gui.md:6
|
|
msgid "We will have a number of widgets in our library:"
|
|
msgstr "라이브러리에는 몇 가지 위젯이 있습니다:"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:8
|
|
msgid ""
|
|
"* `Window`: has a `title` and contains other widgets.\n"
|
|
"* `Button`: has a `label` and a callback function which is invoked when the\n"
|
|
" button is pressed.\n"
|
|
"* `Label`: has a `label`."
|
|
msgstr ""
|
|
"* `Window`: `title` 속성을 가지고 있으며, 다른 위젯들을 포함할 수 있습니다.\n"
|
|
"* `Button`: `label` 속성을 가지고 있으며, 버튼이 눌렸을때 실행되는 콜백 함수가 있습니다.\n"
|
|
"* `Label`: `label` 속성을 가지고 있습니다."
|
|
|
|
#: src/exercises/day-3/simple-gui.md:13
|
|
msgid "The widgets will implement a `Widget` trait, see below."
|
|
msgstr "위젯들은 모두 `Widget` 트레잇을 구현합니다. 아래 코드를 참조하세요."
|
|
|
|
#: src/exercises/day-3/simple-gui.md:15
|
|
msgid ""
|
|
"Copy the code below to <https://play.rust-lang.org/>, fill in the missing\n"
|
|
"`draw_into` methods so that you implement the `Widget` trait:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사하고 누락된 `draw_into`메서드를 채워 넣어 `Widget` 트레잇을 완성해봅시다:"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:18
|
|
msgid ""
|
|
"```rust,should_panic\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_imports, unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"pub trait Widget {\n"
|
|
" /// Natural width of `self`.\n"
|
|
" fn width(&self) -> usize;\n"
|
|
"\n"
|
|
" /// Draw the widget into a buffer.\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write);\n"
|
|
"\n"
|
|
" /// Draw the widget on standard output.\n"
|
|
" fn draw(&self) {\n"
|
|
" let mut buffer = String::new();\n"
|
|
" self.draw_into(&mut buffer);\n"
|
|
" println!(\"{buffer}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Label {\n"
|
|
" label: String,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Label {\n"
|
|
" fn new(label: &str) -> Label {\n"
|
|
" Label {\n"
|
|
" label: label.to_owned(),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Button {\n"
|
|
" label: Label,\n"
|
|
" callback: Box<dyn FnMut()>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Button {\n"
|
|
" fn new(label: &str, callback: Box<dyn FnMut()>) -> Button {\n"
|
|
" Button {\n"
|
|
" label: Label::new(label),\n"
|
|
" callback,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Window {\n"
|
|
" title: String,\n"
|
|
" widgets: Vec<Box<dyn Widget>>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Window {\n"
|
|
" fn new(title: &str) -> Window {\n"
|
|
" Window {\n"
|
|
" title: title.to_owned(),\n"
|
|
" widgets: Vec::new(),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn add_widget(&mut self, widget: Box<dyn Widget>) {\n"
|
|
" self.widgets.push(widget);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"\n"
|
|
"impl Widget for Label {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Widget for Button {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Widget for Window {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut window = Window::new(\"Rust GUI Demo 1.23\");\n"
|
|
" window.add_widget(Box::new(Label::new(\"This is a small text GUI demo.\")));\n"
|
|
" window.add_widget(Box::new(Button::new(\n"
|
|
" \"Click me!\",\n"
|
|
" Box::new(|| println!(\"You clicked the button!\")),\n"
|
|
" )));\n"
|
|
" window.draw();\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/simple-gui.md:123
|
|
msgid "The output of the above program can be something simple like this:"
|
|
msgstr "위 프로그램의 출력은 아래와 같습니다:"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:125
|
|
msgid ""
|
|
"```text\n"
|
|
"========\n"
|
|
"Rust GUI Demo 1.23\n"
|
|
"========\n"
|
|
"\n"
|
|
"This is a small text GUI demo.\n"
|
|
"\n"
|
|
"| Click me! |\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/simple-gui.md:135
|
|
msgid ""
|
|
"If you want to draw aligned text, you can use the\n"
|
|
"[fill/alignment](https://doc.rust-lang.org/std/fmt/index.html#fillalignment)\n"
|
|
"formatting operators. In particular, notice how you can pad with different\n"
|
|
"characters (here a `'/'`) and how you can control alignment:"
|
|
msgstr "텍스트를 줄맞춤 해서 그리려면 [fill/alignment](https://doc.rust-lang.org/std/fmt/index.html#fillalignment)를 참조하시기 바랍니다. 특수 문자(여기서는 `'/'`)로 패딩을 주는 방법과 정렬을 제어하는 방법을 확인하시기 바랍니다:"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:140
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let width = 10;\n"
|
|
" println!(\"left aligned: |{:/<width$}|\", \"foo\");\n"
|
|
" println!(\"centered: |{:/^width$}|\", \"foo\");\n"
|
|
" println!(\"right aligned: |{:/>width$}|\", \"foo\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/simple-gui.md:149
|
|
msgid "Using such alignment tricks, you can for example produce output like this:"
|
|
msgstr "위의 정렬 트릭을 사용하여 다음과 같은 출력을 생성할 수 있습니다:"
|
|
|
|
#: src/exercises/day-3/simple-gui.md:151
|
|
msgid ""
|
|
"```text\n"
|
|
"+--------------------------------+\n"
|
|
"| Rust GUI Demo 1.23 |\n"
|
|
"+================================+\n"
|
|
"| This is a small text GUI demo. |\n"
|
|
"| +-----------+ |\n"
|
|
"| | Click me! | |\n"
|
|
"| +-----------+ |\n"
|
|
"+--------------------------------+\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling.md:1
|
|
msgid "# Error Handling"
|
|
msgstr "# 오류처리"
|
|
|
|
#: src/error-handling.md:3
|
|
msgid "Error handling in Rust is done using explicit control flow:"
|
|
msgstr "러스트에서 오류는 명시적인 흐름을 따라 처리가 됩니다:"
|
|
|
|
#: src/error-handling.md:5
|
|
msgid ""
|
|
"* Functions that can have errors list this in their return type,\n"
|
|
"* There are no exceptions."
|
|
msgstr ""
|
|
"* 오류를 발생할 수 있는 함수는 반환 타입에 이를 명시해야 합니다.\n"
|
|
"* 예외(exception) 기능은 없습니다."
|
|
|
|
#: src/error-handling/panics.md:1
|
|
msgid "# Panics"
|
|
msgstr "# 패닉"
|
|
|
|
#: src/error-handling/panics.md:3
|
|
msgid "Rust will trigger a panic if a fatal error happens at runtime:"
|
|
msgstr "러스트는 수행 중 치명적인 오류를 만나면 패닉을 발생할 것입니다:"
|
|
|
|
#: src/error-handling/panics.md:5
|
|
msgid ""
|
|
"```rust,editable,should_panic\n"
|
|
"fn main() {\n"
|
|
" let v = vec![10, 20, 30];\n"
|
|
" println!(\"v[100]: {}\", v[100]);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/panics.md:12
|
|
msgid ""
|
|
"* Panics are for unrecoverable and unexpected errors.\n"
|
|
" * Panics are symptoms of bugs in the program.\n"
|
|
"* Use non-panicking APIs (such as `Vec::get`) if crashing is not acceptable."
|
|
msgstr ""
|
|
"* 패닉은 복구할 수 없고 예상치 못한 오류입니다.\n"
|
|
" * 패닉은 프로그램에 버그가 있다는 것을 나타냅니다.\n"
|
|
"* 충돌(크래시)을 허용하지 않아야 하는 경우, 패닉을 유발하지 않는 API(`Vec::get`등)를 사용하면 됩니다."
|
|
|
|
#: src/error-handling/panic-unwind.md:1
|
|
msgid "# Catching the Stack Unwinding"
|
|
msgstr "# 스택 되감기"
|
|
|
|
#: src/error-handling/panic-unwind.md:3
|
|
msgid "By default, a panic will cause the stack to unwind. The unwinding can be caught:"
|
|
msgstr "기본적으로, 패닉이 발생하면 스택 되감기를 합니다. 스택 되감기는 다음과 같이 캐치가 가능합니다:"
|
|
|
|
#: src/error-handling/panic-unwind.md:5
|
|
msgid ""
|
|
"```rust\n"
|
|
"use std::panic;\n"
|
|
"\n"
|
|
"let result = panic::catch_unwind(|| {\n"
|
|
" println!(\"hello!\");\n"
|
|
"});\n"
|
|
"assert!(result.is_ok());\n"
|
|
"\n"
|
|
"let result = panic::catch_unwind(|| {\n"
|
|
" panic!(\"oh no!\");\n"
|
|
"});\n"
|
|
"assert!(result.is_err());\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/panic-unwind.md:19
|
|
msgid ""
|
|
"* This can be useful in servers which should keep running even if a single\n"
|
|
" request crashes.\n"
|
|
"* This does not work if `panic = 'abort'` is set in your `Cargo.toml`."
|
|
msgstr ""
|
|
"* 이것은 단일 요청이 크래시 되더라도 프로그램이 계속 실행되야 하는 서버에 유용합니다.\n"
|
|
"* 만약 `Cargo.toml`설정파일에 `panic = abort`을 설정했다면 크래시를 캐치할 수 없습니다."
|
|
|
|
#: src/error-handling/result.md:1
|
|
msgid "# Structured Error Handling with `Result`"
|
|
msgstr "# `Result`를 이용한 구조화된 오류처리"
|
|
|
|
#: src/error-handling/result.md:3
|
|
msgid ""
|
|
"We have already seen the `Result` enum. This is used pervasively when errors are\n"
|
|
"expected as part of normal operation:"
|
|
msgstr "여러분은 이미 `Result` 열거형을 몇 번 봤습니다. 이 타입은 프로그램의 정상적인 수행 중에 발생할 수 있는 오류값들을 나타내기 위해 사용됩니다:"
|
|
|
|
#: src/error-handling/result.md:6
|
|
msgid ""
|
|
"```rust\n"
|
|
"use std::fs::File;\n"
|
|
"use std::io::Read;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let file = File::open(\"diary.txt\");\n"
|
|
" match file {\n"
|
|
" Ok(mut file) => {\n"
|
|
" let mut contents = String::new();\n"
|
|
" file.read_to_string(&mut contents);\n"
|
|
" println!(\"Dear diary: {contents}\");\n"
|
|
" },\n"
|
|
" Err(err) => {\n"
|
|
" println!(\"The diary could not be opened: {err}\");\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/result.md:27
|
|
msgid ""
|
|
" * As with `Option`, the successful value sits inside of `Result`, forcing the developer to\n"
|
|
" explicitly extract it. This encourages error checking. In the case where an error should never happen,\n"
|
|
" `unwrap()` or `expect()` can be called, and this is a signal of the developer intent too. \n"
|
|
" * `Result` documentation is a recommended read. Not during the course, but it is worth mentioning. \n"
|
|
" It contains a lot of convenience methods and functions that help functional-style programming. \n"
|
|
" "
|
|
msgstr ""
|
|
" * `Option`와 마찬가지로, 성공한 경우의 값은 `Result` 내부에 있습니다. 그래서, 개발자는 명시적으로 이를 추출하여야 합니다. 이렇게 함으로써 값을 읽기 전에 오류 발생 여부를 반드시 체크하도록 유도하고 있습니다. 만일 오류가 절대 발생하지 않는 경우라면 `unwrap()`이나 `expect()`를 사용할 수 있으며, 이는 개발자의 의도(_역주_: 오류가 발생할 수 없음)을 명시적으로 나타내는 방법이기도 합니다.\n"
|
|
" * 수업중엔 아니지만 `Result`의 API 레퍼런스를 읽는 것을 권장합니다. 함수형 프로그래밍 스타일에 도움이 되는 편리한 메서드와 함수를 많이 배울 수 있습니다.\n"
|
|
" "
|
|
|
|
#: src/error-handling/try-operator.md:1
|
|
msgid "# Propagating Errors with `?`"
|
|
msgstr "# `?`를 이용한 오류 전파"
|
|
|
|
#: src/error-handling/try-operator.md:3
|
|
msgid ""
|
|
"The try-operator `?` is used to return errors to the caller. It lets you turn\n"
|
|
"the common"
|
|
msgstr "연산자 `?`는 호출자에게 오류를 반환할 때 사용합니다. 이를 이용하면 이런 코드를"
|
|
|
|
#: src/error-handling/try-operator.md:6
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"match some_expression {\n"
|
|
" Ok(value) => value,\n"
|
|
" Err(err) => return Err(err),\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/try-operator.md:13
|
|
msgid "into the much simpler"
|
|
msgstr "이렇게 짧게 쓸 수 있습니다."
|
|
|
|
#: src/error-handling/try-operator.md:15
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"some_expression?\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/try-operator.md:19
|
|
msgid "We can use this to simplify our error handing code:"
|
|
msgstr "이제 우리 예제에 적용해 보겠습니다:"
|
|
|
|
#: src/error-handling/try-operator.md:21
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::fs;\n"
|
|
"use std::io::{self, Read};\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String, io::Error> {\n"
|
|
" let username_file_result = fs::File::open(path);\n"
|
|
"\n"
|
|
" let mut username_file = match username_file_result {\n"
|
|
" Ok(file) => file,\n"
|
|
" Err(e) => return Err(e),\n"
|
|
" };\n"
|
|
"\n"
|
|
" let mut username = String::new();\n"
|
|
"\n"
|
|
" match username_file.read_to_string(&mut username) {\n"
|
|
" Ok(_) => Ok(username),\n"
|
|
" Err(e) => Err(e),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"alice\").unwrap();\n"
|
|
" let username = read_username(\"config.dat\");\n"
|
|
" println!(\"username or error: {username:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/try-operator.md:52
|
|
#: src/error-handling/converting-error-types-example.md:52
|
|
msgid ""
|
|
"* The `username` variable can be either `Ok(string)` or `Err(error)`.\n"
|
|
"* Use the `fs::write` call to test out the different scenarios: no file, empty file, file with username."
|
|
msgstr ""
|
|
"* `username` 변수는 `Ok(string)`이거나 `Err(error)`일 수 있습니다.\n"
|
|
"* `fs::write` 메서드를 사용하여 파일이 없거나, 비었거나, 중복되는 경우 등을 테스트해 봅니다."
|
|
|
|
#: src/error-handling/converting-error-types.md:1
|
|
#: src/error-handling/converting-error-types-example.md:1
|
|
msgid "# Converting Error Types"
|
|
msgstr "# 에러 타입이 다를 경우"
|
|
|
|
#: src/error-handling/converting-error-types.md:3
|
|
msgid "The effective expansion of `?` is a little more complicated than previously indicated:"
|
|
msgstr "실제로 `?`가 적용되는 과정은 아까 설명한 것 보다 좀 더 복잡합니다:"
|
|
|
|
#: src/error-handling/converting-error-types.md:5
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"expression?\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/converting-error-types.md:9
|
|
msgid "works the same as"
|
|
msgstr "위 표현은 아래와 같습니다"
|
|
|
|
#: src/error-handling/converting-error-types.md:11
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"match expression {\n"
|
|
" Ok(value) => value,\n"
|
|
" Err(err) => return Err(From::from(err)),\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/converting-error-types.md:18
|
|
msgid ""
|
|
"The `From::from` call here means we attempt to convert the error type to the\n"
|
|
"type returned by the function:"
|
|
msgstr "`From::from`을 통해 원래의 에러 타입을 이 함수가 반환하는 에러 타입으로 변환하고 있습니다:"
|
|
|
|
#: src/error-handling/converting-error-types-example.md:3
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::error::Error;\n"
|
|
"use std::fmt::{self, Display, Formatter};\n"
|
|
"use std::fs::{self, File};\n"
|
|
"use std::io::{self, Read};\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"enum ReadUsernameError {\n"
|
|
" IoError(io::Error),\n"
|
|
" EmptyUsername(String),\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Error for ReadUsernameError {}\n"
|
|
"\n"
|
|
"impl Display for ReadUsernameError {\n"
|
|
" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n"
|
|
" match self {\n"
|
|
" Self::IoError(e) => write!(f, \"IO error: {}\", e),\n"
|
|
" Self::EmptyUsername(filename) => write!(f, \"Found no username in {}\", filename),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl From<io::Error> for ReadUsernameError {\n"
|
|
" fn from(err: io::Error) -> ReadUsernameError {\n"
|
|
" ReadUsernameError::IoError(err)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String, ReadUsernameError> {\n"
|
|
" let mut username = String::with_capacity(100);\n"
|
|
" File::open(path)?.read_to_string(&mut username)?;\n"
|
|
" if username.is_empty() {\n"
|
|
" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n"
|
|
" }\n"
|
|
" Ok(username)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"\").unwrap();\n"
|
|
" let username = read_username(\"config.dat\");\n"
|
|
" println!(\"username or error: {username:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/converting-error-types-example.md:55
|
|
msgid ""
|
|
"It is good practice for all error types to implement `std::error::Error`, which requires `Debug` and\n"
|
|
"`Display`. It's generally helpful for them to implement `Clone` and `Eq` too where possible, to make\n"
|
|
"life easier for tests and consumers of your library. In this case we can't easily do so, because\n"
|
|
"`io::Error` doesn't implement them."
|
|
msgstr "에러 타입이 `std::error::Error`를 구현하도록 하는 것은 좋은 습관입니다. 그러면 `Debug`와 `Display` 트레잇 또한 구현을 해야 합니다. 가능하다면 `Clone`과 `Eq` 트레잇도 구현하도록 하세요. 여러분의 라이브러리가 테스트 하기 쉬워지고, 사용하기 좋아질 겁니다. 다만, 이 예제에서는 그렇게 하기 힘듭니다. 왜냐하면 `io::Error`는 이 트레잇들을 구현하고 있지 않기 때문입니다."
|
|
|
|
#: src/error-handling/deriving-error-enums.md:1
|
|
msgid "# Deriving Error Enums"
|
|
msgstr "# 또다른 오류 열거형"
|
|
|
|
#: src/error-handling/deriving-error-enums.md:3
|
|
msgid ""
|
|
"The [thiserror](https://docs.rs/thiserror/) crate is a popular way to create an\n"
|
|
"error enum like we did on the previous page:"
|
|
msgstr "[thiserror](https://docs.rs/thiserror/)는, 이전 페이지에서 보았던 것과 같은 에러 열거형을 쉽게 만들 수 있게 해 주는 유명한 크레이트 입니다:"
|
|
|
|
#: src/error-handling/deriving-error-enums.md:6
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::{fs, io};\n"
|
|
"use std::io::Read;\n"
|
|
"use thiserror::Error;\n"
|
|
"\n"
|
|
"#[derive(Debug, Error)]\n"
|
|
"enum ReadUsernameError {\n"
|
|
" #[error(\"Could not read: {0}\")]\n"
|
|
" IoError(#[from] io::Error),\n"
|
|
" #[error(\"Found no username in {0}\")]\n"
|
|
" EmptyUsername(String),\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String, ReadUsernameError> {\n"
|
|
" let mut username = String::with_capacity(100);\n"
|
|
" fs::File::open(path)?.read_to_string(&mut username)?;\n"
|
|
" if username.is_empty() {\n"
|
|
" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n"
|
|
" }\n"
|
|
" Ok(username)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"\").unwrap();\n"
|
|
" match read_username(\"config.dat\") {\n"
|
|
" Ok(username) => println!(\"Username: {username}\"),\n"
|
|
" Err(err) => println!(\"Error: {err}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/deriving-error-enums.md:39
|
|
msgid ""
|
|
"`thiserror`'s derive macro automatically implements `std::error::Error`, and optionally `Display`\n"
|
|
"(if the `#[error(...)]` attributes are provided) and `From` (if the `#[from]` attribute is added).\n"
|
|
"It also works for structs."
|
|
msgstr "`thiserror`의 derive 매크로를 이용하면 `std::error::Error`과 `Display`(만약 `#[error(...)]` 어트리뷰트를 추가했을 경우), `From`(만약 `#[from]` 어트리뷰트를 추가했을 경우) 트레잇들이 자동으로 구현이 됩니다. 구조체에 대해서도 사용 가능합니다."
|
|
|
|
#: src/error-handling/deriving-error-enums.md:43
|
|
msgid "It doesn't affect your public API, which makes it good for libraries."
|
|
msgstr "이 매크로를 사용해도 밖으로 노출되는 API가 변경되지는 않습니다. 라이브러리를 만들 경우에는 이게 중요하죠."
|
|
|
|
#: src/error-handling/dynamic-errors.md:1
|
|
msgid "# Dynamic Error Types"
|
|
msgstr "# 동적인 에러 타입"
|
|
|
|
#: src/error-handling/dynamic-errors.md:3
|
|
msgid ""
|
|
"Sometimes we want to allow any type of error to be returned without writing our own enum covering\n"
|
|
"all the different possibilities. `std::error::Error` makes this easy."
|
|
msgstr "때때로 우리는, 발생 가능한 모든 에러를 일일히 열거하지 않고, 어떤 종류의 에러라도 상관없이 리턴하고 싶을 때가 있습니다. `std::error::Error`를 이용하면 쉽습니다."
|
|
|
|
#: src/error-handling/dynamic-errors.md:6
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::fs::{self, File};\n"
|
|
"use std::io::Read;\n"
|
|
"use thiserror::Error;\n"
|
|
"use std::error::Error;\n"
|
|
"\n"
|
|
"#[derive(Clone, Debug, Eq, Error, PartialEq)]\n"
|
|
"#[error(\"Found no username in {0}\")]\n"
|
|
"struct EmptyUsernameError(String);\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String, Box<dyn Error>> {\n"
|
|
" let mut username = String::with_capacity(100);\n"
|
|
" File::open(path)?.read_to_string(&mut username)?;\n"
|
|
" if username.is_empty() {\n"
|
|
" return Err(EmptyUsernameError(String::from(path)).into());\n"
|
|
" }\n"
|
|
" Ok(username)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"\").unwrap();\n"
|
|
" match read_username(\"config.dat\") {\n"
|
|
" Ok(username) => println!(\"Username: {username}\"),\n"
|
|
" Err(err) => println!(\"Error: {err}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/error-handling/dynamic-errors.md:36
|
|
msgid ""
|
|
"This saves on code, but gives up the ability to cleanly handle different error cases differently in\n"
|
|
"the program. As such it's generally not a good idea to use `Box<dyn Error>` in the public API of a\n"
|
|
"library, but it can be a good option in a program where you just want to display the error message\n"
|
|
"somewhere."
|
|
msgstr "이렇게 하면 코드의 양을 줄일 수 있습니다. 그러나 서로 다른 종류의 에러를 구별하여 다르게 처리하는 것이 불가능해 집니다. 때문에, `Box<dyn Error>`를 라이브러리의 API로 노출하는게 좋은 디자인은 아닙니다. 에러 발생 시, 그저 에러 메시지를 출력하고 싶은 경우와 같이 제한된 상황에서는 유용할 수 있습니다."
|
|
|
|
#: src/error-handling/error-contexts.md:1
|
|
msgid "# Adding Context to Errors"
|
|
msgstr "# 오류에 문맥 추가"
|
|
|
|
#: src/error-handling/error-contexts.md:3
|
|
msgid ""
|
|
"The widely used [anyhow](https://docs.rs/anyhow/) crate can help you add\n"
|
|
"contextual information to your errors and allows you to have fewer\n"
|
|
"custom error types:"
|
|
msgstr "[anyhow](https://docs.rs/anyhow/) 크레이트는 에러에 에러가 발생한 문맥에 대한 정보를 추가하기 위해 널리 사용되며, 이를 이용하면 서로 다른 문맥을 나타내기 사용자 정의 오류 타입을 많이 만들어야 하는 불편함을 피할 수 있습니다:"
|
|
|
|
#: src/error-handling/error-contexts.md:7
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::{fs, io};\n"
|
|
"use std::io::Read;\n"
|
|
"use anyhow::{Context, Result, bail};\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String> {\n"
|
|
" let mut username = String::with_capacity(100);\n"
|
|
" fs::File::open(path)\n"
|
|
" .with_context(|| format!(\"Failed to open {path}\"))?\n"
|
|
" .read_to_string(&mut username)\n"
|
|
" .context(\"Failed to read\")?;\n"
|
|
" if username.is_empty() {\n"
|
|
" bail!(\"Found no username in {path}\");\n"
|
|
" }\n"
|
|
" Ok(username)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"\").unwrap();\n"
|
|
" match read_username(\"config.dat\") {\n"
|
|
" Ok(username) => println!(\"Username: {username}\"),\n"
|
|
" Err(err) => println!(\"Error: {err:?}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::{fs, io};\n"
|
|
"use std::io::Read;\n"
|
|
"use anyhow::{Context, Result, bail};\n"
|
|
"\n"
|
|
"fn read_username(path: &str) -> Result<String> {\n"
|
|
" let mut username = String::with_capacity(100);\n"
|
|
" fs::File::open(path)\n"
|
|
" .with_context(|| format!(\"Failed to open {path}\"))?\n"
|
|
" .read_to_string(&mut username)\n"
|
|
" .context(\"Failed to read\")?;\n"
|
|
" if username.is_empty() {\n"
|
|
" bail!(\"Found no username in {path}\");\n"
|
|
" }\n"
|
|
" Ok(username)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" //fs::write(\"config.dat\", \"\").unwrap();\n"
|
|
" match read_username(\"config.dat\") {\n"
|
|
" Ok(username) => println!(\"Username: {username}\"),\n"
|
|
" Err(err) => println!(\"Error: {err:?}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/error-handling/error-contexts.md:35
|
|
msgid ""
|
|
"* `anyhow::Result<V>` is a type alias for `Result<V, anyhow::Error>`.\n"
|
|
"* `anyhow::Error` is essentially a wrapper around `Box<dyn Error>`. As such it's again generally not\n"
|
|
" a good choice for the public API of a library, but is widely used in applications.\n"
|
|
"* Actual error type inside of it can be extracted for examination if necessary.\n"
|
|
"* Functionality provided by `anyhow::Result<T>` may be familiar to Go developers, as it provides\n"
|
|
" similar usage patterns and ergonomics to `(T, error)` from Go."
|
|
msgstr ""
|
|
"* `anyhow::Result<V>`는 `Result<V, anyhow::Error>`의 타입 앨리어스(alias)입니다.\n"
|
|
"* `anyhow::Error`는 `Box<dyn Error>`의 래퍼 타입이라 할 수 있습니다. 따라서 라이브러리의 공개 API로서 사용하기에 부적합하다고 할 수 있지만 많은 애플리케이션에 널리 사용되고 있습니다.\n"
|
|
"* 필요하다면 `anyhow::Error`에 저장된 진짜 에러 타입을 꺼내어 검사할 수도 있습니다.\n"
|
|
"* `anyhow::Result<T>`가 제공하는 기능들이 Go 언어 개발자들에게는 익숙할 것입니다. Go언어에서 반환 값으로 사용하는 `(T, error)` 패턴과 비슷하기 때문입니다."
|
|
|
|
#: src/testing.md:1
|
|
msgid "# Testing"
|
|
msgstr "# 테스팅"
|
|
|
|
#: src/testing.md:3
|
|
msgid "Rust and Cargo come with a simple unit test framework:"
|
|
msgstr "러스트와 카고(cargo)는 간단한 단위 테스트 프레임워크와 함께 제공됩니다:"
|
|
|
|
#: src/testing.md:5
|
|
msgid ""
|
|
"* Unit tests are supported throughout your code.\n"
|
|
"\n"
|
|
"* Integration tests are supported via the `tests/` directory."
|
|
msgstr ""
|
|
"* 단위 테스트는 코드 전반에서 지원됩니다.\n"
|
|
"\n"
|
|
"* 통합 테스트는 `tests/` 디렉터리를 통해 지원됩니다."
|
|
|
|
#: src/testing/unit-tests.md:1
|
|
msgid "# Unit Tests"
|
|
msgstr "# 단위 테스트"
|
|
|
|
#: src/testing/unit-tests.md:3
|
|
msgid "Mark unit tests with `#[test]`:"
|
|
msgstr "단위 테스트는 `#[test]` 로 표시합니다:"
|
|
|
|
#: src/testing/unit-tests.md:5
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"fn first_word(text: &str) -> &str {\n"
|
|
" match text.find(' ') {\n"
|
|
" Some(idx) => &text[..idx],\n"
|
|
" None => &text,\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_empty() {\n"
|
|
" assert_eq!(first_word(\"\"), \"\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_single_word() {\n"
|
|
" assert_eq!(first_word(\"Hello\"), \"Hello\");\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_multiple_words() {\n"
|
|
" assert_eq!(first_word(\"Hello World\"), \"Hello\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/testing/unit-tests.md:29
|
|
msgid "Use `cargo test` to find and run the unit tests."
|
|
msgstr "`cargo test` 커맨드를 사용하면 단위 테스트를 찾아서 실행합니다."
|
|
|
|
#: src/testing/test-modules.md:1
|
|
msgid "# Test Modules"
|
|
msgstr "# 테스트 모듈"
|
|
|
|
#: src/testing/test-modules.md:3
|
|
msgid ""
|
|
"Unit tests are often put in a nested module (run tests on the\n"
|
|
"[Playground](https://play.rust-lang.org/)):"
|
|
msgstr "단위 테스트는 원래 모듈 밑에 서브 모듈로 만드는 경우가 많습니다. ([플레이그라운드](https://play.rust-lang.org/)에서 다음 테스트를 수행해 보세요):"
|
|
|
|
#: src/testing/test-modules.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn helper(a: &str, b: &str) -> String {\n"
|
|
" format!(\"{a} {b}\")\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub fn main() {\n"
|
|
" println!(\"{}\", helper(\"Hello\", \"World\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[cfg(test)]\n"
|
|
"mod tests {\n"
|
|
" use super::*;\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_helper() {\n"
|
|
" assert_eq!(helper(\"foo\", \"bar\"), \"foo bar\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/testing/test-modules.md:26
|
|
msgid ""
|
|
"* This lets you unit test private helpers.\n"
|
|
"* The `#[cfg(test)]` attribute is only active when you run `cargo test`."
|
|
msgstr ""
|
|
"* 이렇게 서브 모듈로 테스트를 만들면 private한 헬퍼 함수에 대한 단위 테스트도 가능합니다.\n"
|
|
"* `#[cfg(test)]` 어트리뷰트가 추가된 항목은 `cargo test`를 수행했을 경우에만 동작합니다."
|
|
|
|
#: src/testing/doc-tests.md:1
|
|
msgid "# Documentation Tests"
|
|
msgstr "# 문서화주석 테스트"
|
|
|
|
#: src/testing/doc-tests.md:3
|
|
msgid "Rust has built-in support for documentation tests:"
|
|
msgstr "러스트는 문서화주석에 대한 테스트를 내장하여 제공합니다:"
|
|
|
|
#: src/testing/doc-tests.md:5
|
|
msgid ""
|
|
"```rust\n"
|
|
"/// Shortens a string to the given length.\n"
|
|
"///\n"
|
|
"/// ```\n"
|
|
"/// use playground::shorten_string;\n"
|
|
"/// assert_eq!(shorten_string(\"Hello World\", 5), \"Hello\");\n"
|
|
"/// assert_eq!(shorten_string(\"Hello World\", 20), \"Hello World\");\n"
|
|
"/// ```\n"
|
|
"pub fn shorten_string(s: &str, length: usize) -> &str {\n"
|
|
" &s[..std::cmp::min(length, s.len())]\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust\n"
|
|
"/// Shortens a string to the given length.\n"
|
|
"///\n"
|
|
"/// ```\n"
|
|
"/// use playground::shorten_string;\n"
|
|
"/// assert_eq!(shorten_string(\"Hello World\", 5), \"Hello\");\n"
|
|
"/// assert_eq!(shorten_string(\"Hello World\", 20), \"Hello World\");\n"
|
|
"/// ```\n"
|
|
"pub fn shorten_string(s: &str, length: usize) -> &str {\n"
|
|
" &s[..std::cmp::min(length, s.len())]\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/testing/doc-tests.md:18
|
|
msgid ""
|
|
"* Code blocks in `///` comments are automatically seen as Rust code.\n"
|
|
"* The code will be compiled and executed as part of `cargo test`.\n"
|
|
"* Test the above code on the [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0)."
|
|
msgstr ""
|
|
"* `///` 주석안의 코드 블록은 자동으로 러스트 코드로 인식됩니다.\n"
|
|
"* 이 코드 블록은 `cargo test` 호출하면 자동으로 컴파일되고 실행됩니다.\n"
|
|
"* 위 코드를 [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0)에서 테스트 해 보시기 바랍니다."
|
|
|
|
#: src/testing/integration-tests.md:1
|
|
msgid "# Integration Tests"
|
|
msgstr "# 통합 테스트"
|
|
|
|
#: src/testing/integration-tests.md:3
|
|
msgid "If you want to test your library as a client, use an integration test."
|
|
msgstr "라이브러리를 사용자 입장에서 테스트 하려면, 통합 테스트를 해야 합니다."
|
|
|
|
#: src/testing/integration-tests.md:5
|
|
msgid "Create a `.rs` file under `tests/`:"
|
|
msgstr "`test/`디렉터리 아래에 `.rs`파일을 하나 만드세요:"
|
|
|
|
#: src/testing/integration-tests.md:7
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"use my_library::init;\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_init() {\n"
|
|
" assert!(init().is_ok());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,ignore\n"
|
|
"use my_library::init;\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_init() {\n"
|
|
" assert!(init().is_ok());\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/testing/integration-tests.md:16
|
|
msgid "These tests only have access to the public API of your crate."
|
|
msgstr "이 테스트는 크레이트의 공개 API에만 접근할 수 있습니다."
|
|
|
|
#: src/unsafe.md:1
|
|
msgid "# Unsafe Rust"
|
|
msgstr "# 안전하지 않은 러스트"
|
|
|
|
#: src/unsafe.md:3
|
|
msgid "The Rust language has two parts:"
|
|
msgstr "러스트로 작성된 프로그램은 크게 두 부분으로 나뉩니다:"
|
|
|
|
#: src/unsafe.md:5
|
|
msgid ""
|
|
"* **Safe Rust:** memory safe, no undefined behavior possible.\n"
|
|
"* **Unsafe Rust:** can trigger undefined behavior if preconditions are violated."
|
|
msgstr ""
|
|
"* **안전한 러스트:** 메모리 관련 오류 발생 불가능, 정의되지 않은 동작 없음.\n"
|
|
"* **안전하지 않은 러스트:** 특별한 조건을 만족하지 않은채로 사용되면 정의되지 않은 동작을 유발할 수 있음."
|
|
|
|
#: src/unsafe.md:8
|
|
msgid ""
|
|
"We will be seeing mostly safe Rust in this course, but it's important to know\n"
|
|
"what Unsafe Rust is."
|
|
msgstr "이 강의는 대부분 안전한 러스트에 대해 다루지만 안전하지 않은 러스트가 무엇인지는 알아 두어야 합니다."
|
|
|
|
#: src/unsafe.md:11
|
|
msgid ""
|
|
"Unsafe code is usually small and isolated, and its correctness should be carefully\n"
|
|
"documented. It is usually wrapped in a safe abstraction layer."
|
|
msgstr "보통, 안전하지 않은 러스트 코드는 크기가 작으며, 독립적으로 존재합니다. 그리고 코드가 왜 잘 작동하는지에 대해 세밀하게 문서화가 되어 있습니다. 그리고, 많은 경우 안전한 러스트 코드를 통해서 추상화를 시킨 후 사용하게 됩니다."
|
|
|
|
#: src/unsafe.md:14
|
|
msgid "Unsafe Rust gives you access to five new capabilities:"
|
|
msgstr "안전하지 않은 러스트를 이용하면 다음과 같은 다섯 가지 것들이 가능해 집니다:"
|
|
|
|
#: src/unsafe.md:16
|
|
msgid ""
|
|
"* Dereference raw pointers.\n"
|
|
"* Access or modify mutable static variables.\n"
|
|
"* Access `union` fields.\n"
|
|
"* Call `unsafe` functions, including `extern` functions.\n"
|
|
"* Implement `unsafe` traits."
|
|
msgstr ""
|
|
"* 원시 포인터 역참조(따라가기)\n"
|
|
"* 정적 가변변수 접근 및 수정\n"
|
|
"* `union` 필드 접근\n"
|
|
"* `extern` 함수를 포함한 `unsafe` 함수 호출\n"
|
|
"* `unsafe` 트레잇 구현."
|
|
|
|
#: src/unsafe.md:22
|
|
msgid ""
|
|
"We will briefly cover unsafe capabilities next. For full details, please see\n"
|
|
"[Chapter 19.1 in the Rust Book](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html)\n"
|
|
"and the [Rustonomicon](https://doc.rust-lang.org/nomicon/)."
|
|
msgstr "위 기능들에 대해 간략히 살펴보겠습니다. 자세한 내용은 [러스트 프로그래밍 언어, 19.1절](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html)과 [Rustonomicon](https://doc.rust-lang.org/nomicon/)를 참조하세요."
|
|
|
|
#: src/unsafe.md:28
|
|
msgid ""
|
|
"Unsafe Rust does not mean the code is incorrect. It means that developers have\n"
|
|
"turned off the compiler safety features and have to write correct code by\n"
|
|
"themselves. It means the compiler no longer enforces Rust's memory-safety rules."
|
|
msgstr "안전하지 않은 러스트라고 해서 코드가 부정확 하다는 뜻은 아닙니다. 여기서 안전하지 않다의 의미는 컴파일러가 제공해주는 안전 장치들이 꺼진 상태이며, 개발자가 스스로 정확하고 안전한 코드를 작성해야 함을 의미합니다. 이는 컴파일러가 더 이상 러스트의 메모리 안전과 관련된 규칙들을 적용하지 않는다는 것입니다."
|
|
|
|
#: src/unsafe/raw-pointers.md:1
|
|
msgid "# Dereferencing Raw Pointers"
|
|
msgstr "# 원시 포인터 역참조(따라가기)"
|
|
|
|
#: src/unsafe/raw-pointers.md:3
|
|
msgid "Creating pointers is safe, but dereferencing them requires `unsafe`:"
|
|
msgstr "포인터를 만드는 것은 안전합니다. 하지만 역참조(따라가기)할 경우 `unsafe`가 필요합니다:"
|
|
|
|
#: src/unsafe/raw-pointers.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut num = 5;\n"
|
|
"\n"
|
|
" let r1 = &mut num as *mut i32;\n"
|
|
" let r2 = r1 as *const i32;\n"
|
|
"\n"
|
|
" // Safe because r1 and r2 were obtained from references and so are guaranteed to be non-null and\n"
|
|
" // properly aligned, the objects underlying the references from which they were obtained are\n"
|
|
" // live throughout the whole unsafe block, and they are not accessed either through the\n"
|
|
" // references or concurrently through any other pointers.\n"
|
|
" unsafe {\n"
|
|
" println!(\"r1 is: {}\", *r1);\n"
|
|
" *r1 = 10;\n"
|
|
" println!(\"r2 is: {}\", *r2);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let mut num = 5;\n"
|
|
"\n"
|
|
" let r1 = &mut num as *mut i32;\n"
|
|
" let r2 = r1 as *const i32;\n"
|
|
"\n"
|
|
" // 아래 코드는 안전합니다. r1과 r2는 참조로 부터 만들어 졌기 때문에 null이 아니며\n"
|
|
" // align이 맞다는 것이 보장됩니다. 참조가 가리키는 객체들은 unsafe 블럭이 수행되는\n"
|
|
" // 동안 메모리에 살아있습니다. 그리고 이 객체들은 r1과 r2가 아닌 다른 어떤 참조나\n"
|
|
" // 포인터로도 접근이 안되고 있습니다.\n"
|
|
" unsafe {\n"
|
|
" println!(\"r1 is: {}\", *r1);\n"
|
|
" *r1 = 10;\n"
|
|
" println!(\"r2 is: {}\", *r2);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/raw-pointers.md:26
|
|
msgid ""
|
|
"It is good practice (and required by the Android Rust style guide) to write a comment for each\n"
|
|
"`unsafe` block explaining how the code inside it satisfies the safety requirements of the unsafe\n"
|
|
"operations it is doing."
|
|
msgstr "모든 `unsafe` 블록에 대해 왜 그 코드가 안전한지에 대한 설명을 주석으로 다는 것은 좋은 습관입니다(사실 안드로이드의 러스트 스타일 가이드에서는 이게 필수입니다)."
|
|
|
|
#: src/unsafe/raw-pointers.md:30
|
|
msgid ""
|
|
"In the case of pointer dereferences, this means that the pointers must be\n"
|
|
"[_valid_](https://doc.rust-lang.org/std/ptr/index.html#safety), i.e.:"
|
|
msgstr "포인터 역참조를 할 경우, 포인터가 [_유효_](https://doc.rust-lang.org/std/ptr/index.html#safety)해야 합니다. 예를 들어:"
|
|
|
|
#: src/unsafe/raw-pointers.md:33
|
|
msgid ""
|
|
" * The pointer must be non-null.\n"
|
|
" * The pointer must be _dereferenceable_ (within the bounds of a single allocated object).\n"
|
|
" * The object must not have been deallocated.\n"
|
|
" * There must not be concurrent accesses to the same location.\n"
|
|
" * If the pointer was obtained by casting a reference, the underlying object must be live and no\n"
|
|
" reference may be used to access the memory."
|
|
msgstr ""
|
|
" * 포인터는 null이면 안됩니다.\n"
|
|
" * 포인터는 따라가기가 가능해야 합니다 (객체의 어느 한 부분을 가리키고 있어야 합니다).\n"
|
|
" * 이미 반환된 객체를 가리키면 안됩니다.\n"
|
|
" * 같은 위치에 대해 동시적인 접근이 있으면 안됩니다.\n"
|
|
" * 참조를 캐스팅 해서 포인터를 만들었다면, 그 참조가 가리키는 객체는 살아 있어야 하며, 그 객체의 메모리를 접근하는 참조가 하나도 없어야 합니다."
|
|
|
|
#: src/unsafe/raw-pointers.md:40
|
|
msgid "In most cases the pointer must also be properly aligned."
|
|
msgstr "대부분의 경우 포인터는 align되어 있어야 합니다."
|
|
|
|
#: src/unsafe/mutable-static-variables.md:1
|
|
msgid "# Mutable Static Variables"
|
|
msgstr "# 정적 가변 변수"
|
|
|
|
#: src/unsafe/mutable-static-variables.md:3
|
|
msgid "It is safe to read an immutable static variable:"
|
|
msgstr "불변 정적변수를 읽는 것은 안전합니다:"
|
|
|
|
#: src/unsafe/mutable-static-variables.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"static HELLO_WORLD: &str = \"Hello, world!\";\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"HELLO_WORLD: {HELLO_WORLD}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"static HELLO_WORLD: &str = \"Hello, world!\";\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" println!(\"HELLO_WORLD: {HELLO_WORLD}\");\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/mutable-static-variables.md:13
|
|
msgid ""
|
|
"However, since data races can occur, it is unsafe to read and write mutable\n"
|
|
"static variables:"
|
|
msgstr "하지만, 데이터 레이스가 발생할 수 있으므로 정적 가변변수를 읽고 쓰는 것은 안전하지 않습니다:"
|
|
|
|
#: src/unsafe/mutable-static-variables.md:16
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"static mut COUNTER: u32 = 0;\n"
|
|
"\n"
|
|
"fn add_to_counter(inc: u32) {\n"
|
|
" unsafe { COUNTER += inc; } // Potential data race!\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" add_to_counter(42);\n"
|
|
"\n"
|
|
" unsafe { println!(\"COUNTER: {COUNTER}\"); } // Potential data race!\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/unsafe/mutable-static-variables.md:32
|
|
msgid ""
|
|
"Using a mutable static is generally a bad idea, but there are some cases where it might make sense\n"
|
|
"in low-level `no_std` code, such as implementing a heap allocator or working with some C APIs."
|
|
msgstr "일반적으로 이야기 해서, 정적 가변 변수를 쓰는 것은 좋은 아이디어가 아닙니다. 그러나 `no_std`와 같은 저수준 코딩을 할 경우에는 필요하기도 합니다. 예를 들어 힙 할당기를 구현하거나, C API를 사용하는 게 그런 경우입니다."
|
|
|
|
#: src/unsafe/unions.md:1
|
|
msgid "# Unions"
|
|
msgstr "# 유니온"
|
|
|
|
#: src/unsafe/unions.md:3
|
|
msgid "Unions are like enums, but you need to track the active field yourself:"
|
|
msgstr "유니온 타입은 열거형(enum)과 비슷하지만, 어떤 필드에 해당하는 값을 가지고 있는지 여부를 프로그래머가 수동으로 추적해야 합니다:"
|
|
|
|
#: src/unsafe/unions.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"#[repr(C)]\n"
|
|
"union MyUnion {\n"
|
|
" i: u8,\n"
|
|
" b: bool,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let u = MyUnion { i: 42 };\n"
|
|
" println!(\"int: {}\", unsafe { u.i });\n"
|
|
" println!(\"bool: {}\", unsafe { u.b }); // Undefined behavior!\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"#[repr(C)]\n"
|
|
"union MyUnion {\n"
|
|
" i: u8,\n"
|
|
" b: bool,\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let u = MyUnion { i: 42 };\n"
|
|
" println!(\"int: {}\", unsafe { u.i });\n"
|
|
" println!(\"bool: {}\", unsafe { u.b }); // Undefined behavior!\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/unions.md:21
|
|
msgid ""
|
|
"Unions are very rarely needed in Rust as you can usually use an enum. They are occasionally needed\n"
|
|
"for interacting with C library APIs."
|
|
msgstr "러스트에는 열거형이 있기 때문에 유니온이 필요한 경우는 극히 드뭅니다. 유니온은 C 라이브러리 API를 사용할 때 가끔 필요합니다."
|
|
|
|
#: src/unsafe/unions.md:24
|
|
msgid ""
|
|
"If you just want to reinterpret bytes as a different type, you probably want\n"
|
|
"[`std::mem::transmute`](https://doc.rust-lang.org/stable/std/mem/fn.transmute.html) or a safe\n"
|
|
"wrapper such as the [`zerocopy`](https://crates.io/crates/zerocopy) crate."
|
|
msgstr "바이트들을 특정 타입으로 재해석 하고 싶다면 [`std::mem::transmute`](https://doc.rust-lang.org/stable/std/mem/fn.transmute.html)나 좀 더 안전한 [`zerocopy`](https://crates.io/crates/zerocopy) 크레이트를 사용하세요."
|
|
|
|
#: src/unsafe/calling-unsafe-functions.md:1
|
|
msgid "# Calling Unsafe Functions"
|
|
msgstr "# '안전하지 않은' 함수 호출"
|
|
|
|
#: src/unsafe/calling-unsafe-functions.md:3
|
|
msgid ""
|
|
"A function or method can be marked `unsafe` if it has extra preconditions you\n"
|
|
"must uphold to avoid undefined behaviour:"
|
|
msgstr "함수나 메서드가 정의되지 않은 동작으로 빠지지 않게 하기 위해서 만족해야 하는 전제 조건이 있는 경우, 그 함수나 메서드를 `unsafe`로 표시할 수 있습니다:"
|
|
|
|
#: src/unsafe/calling-unsafe-functions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"fn main() {\n"
|
|
" let emojis = \"🗻∈🌏\";\n"
|
|
"\n"
|
|
" // Safe because the indices are in the correct order, within the bounds of\n"
|
|
" // the string slice, and lie on UTF-8 sequence boundaries.\n"
|
|
" unsafe {\n"
|
|
" println!(\"emoji: {}\", emojis.get_unchecked(0..4));\n"
|
|
" println!(\"emoji: {}\", emojis.get_unchecked(4..7));\n"
|
|
" println!(\"emoji: {}\", emojis.get_unchecked(7..11));\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"char count: {}\", count_chars(unsafe { emojis.get_unchecked(0..7) }));\n"
|
|
"\n"
|
|
" // Not upholding the UTF-8 encoding requirement breaks memory safety!\n"
|
|
" // println!(\"emoji: {}\", unsafe { emojis.get_unchecked(0..3) });\n"
|
|
" // println!(\"char count: {}\", count_chars(unsafe { emojis.get_unchecked(0..3) }));\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn count_chars(s: &str) -> usize {\n"
|
|
" s.chars().map(|_| 1).sum()\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/unsafe/writing-unsafe-functions.md:1
|
|
msgid "# Writing Unsafe Functions"
|
|
msgstr "# '안전하지 않은' 함수 작성하기"
|
|
|
|
#: src/unsafe/writing-unsafe-functions.md:3
|
|
msgid ""
|
|
"You can mark your own functions as `unsafe` if they require particular conditions to avoid undefined\n"
|
|
"behaviour."
|
|
msgstr "여러분이 작성한 함수를 사용할 때 어떤 특별한 조건을 만족해야 한다면, `unsafe`로 마킹할 수 있습니다."
|
|
|
|
#: src/unsafe/writing-unsafe-functions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"/// Swaps the values pointed to by the given pointers.\n"
|
|
"///\n"
|
|
"/// # Safety\n"
|
|
"///\n"
|
|
"/// The pointers must be valid and properly aligned.\n"
|
|
"unsafe fn swap(a: *mut u8, b: *mut u8) {\n"
|
|
" let temp = *a;\n"
|
|
" *a = *b;\n"
|
|
" *b = temp;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut a = 42;\n"
|
|
" let mut b = 66;\n"
|
|
"\n"
|
|
" // Safe because ...\n"
|
|
" unsafe {\n"
|
|
" swap(&mut a, &mut b);\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"a = {}, b = {}\", a, b);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"/// Swaps the values pointed to by the given pointers.\n"
|
|
"///\n"
|
|
"/// # Safety\n"
|
|
"///\n"
|
|
"/// The pointers must be valid and properly aligned.\n"
|
|
"unsafe fn swap(a: *mut u8, b: *mut u8) {\n"
|
|
" let temp = *a;\n"
|
|
" *a = *b;\n"
|
|
" *b = temp;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut a = 42;\n"
|
|
" let mut b = 66;\n"
|
|
"\n"
|
|
" // Safe because ...\n"
|
|
" unsafe {\n"
|
|
" swap(&mut a, &mut b);\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"a = {}, b = {}\", a, b);\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/writing-unsafe-functions.md:33
|
|
msgid "We wouldn't actually use pointers for this because it can be done safely with references."
|
|
msgstr "참조를 사용하면 안전하게 구현할 수 있기 때문에, 실제로 포인터를 사용할 필요는 없습니다."
|
|
|
|
#: src/unsafe/writing-unsafe-functions.md:35
|
|
msgid ""
|
|
"Note that unsafe code is allowed within an unsafe function without an `unsafe` block. We can\n"
|
|
"prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. Try adding it and see what happens."
|
|
msgstr "unsafe 코드가 unsafe 함수의 내부에서 호출될 경우에는 `unsafe`블록을 지정하지 않아도 됩니다. `unsafe`블록을 항상 지정하도록 하고 싶다면 `#[deny(unsafe_op_in_unsafe_fn)]`를 이용하세요. 이 어트리뷰트를 추가해 보고 어떤 일이 일어나는지 확인해 보세요."
|
|
|
|
#: src/unsafe/extern-functions.md:1
|
|
msgid "# Calling External Code"
|
|
msgstr "# 외부 코드 호출"
|
|
|
|
#: src/unsafe/extern-functions.md:3
|
|
msgid ""
|
|
"Functions from other languages might violate the guarantees of Rust. Calling\n"
|
|
"them is thus unsafe:"
|
|
msgstr "다른 언어의 함수는 러스트의 보증을 위반할 수 있습니다. 따라서 이를 호출하는 것은 안전하지 않습니다:"
|
|
|
|
#: src/unsafe/extern-functions.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"extern \"C\" {\n"
|
|
" fn abs(input: i32) -> i32;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" unsafe {\n"
|
|
" // Undefined behavior if abs misbehaves.\n"
|
|
" println!(\"Absolute value of -3 according to C: {}\", abs(-3));\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"extern \"C\" {\n"
|
|
" fn abs(input: i32) -> i32;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" unsafe {\n"
|
|
" // Undefined behavior if abs misbehaves.\n"
|
|
" println!(\"Absolute value of -3 according to C: {}\", abs(-3));\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/extern-functions.md:21
|
|
msgid ""
|
|
"This is usually only a problem for extern functions which do things with pointers which might\n"
|
|
"violate Rust's memory model, but in general any C function might have undefined behaviour under any\n"
|
|
"arbitrary circumstances."
|
|
msgstr "이게 문제가 되는 경우는 대부분 외부 함수가 러스트의 메모리 모델을 위반하고 있을 경우입니다. 그러나 어떤 C함수라도 어떤 임의의 상황에서는 정의되지 않은 동작을 할 수 있기 때문에, 엄밀히 말해서는 모든 외부 함수에 대해서 문제입니다."
|
|
|
|
#: src/unsafe/extern-functions.md:25
|
|
msgid ""
|
|
"The `\"C\"` in this example is the ABI;\n"
|
|
"[other ABIs are available too](https://doc.rust-lang.org/reference/items/external-blocks.html)."
|
|
msgstr "위 예제 코드에서 `\"C\"`는 ABI를 의미합니다. [다른 ABI도 있습니다.](https://doc.rust-lang.org/reference/items/external-blocks.html)"
|
|
|
|
#: src/unsafe/unsafe-traits.md:1
|
|
msgid "# Implementing Unsafe Traits"
|
|
msgstr "# 안전하지 않은 트레잇 구현하기"
|
|
|
|
#: src/unsafe/unsafe-traits.md:3
|
|
msgid ""
|
|
"Like with functions, you can mark a trait as `unsafe` if the implementation must guarantee\n"
|
|
"particular conditions to avoid undefined behaviour."
|
|
msgstr "함수에서와 마찬가지로 트레잇도 `unsafe`로 마킹 가능합니다. 만약 그 트레잇을 구현할 때 정의되지 않은 동작을 피하기 위해 특별한 조건이 필요하다면 말이지요."
|
|
|
|
#: src/unsafe/unsafe-traits.md:6
|
|
msgid ""
|
|
"For example, the `zerocopy` crate has an unsafe trait that looks\n"
|
|
"[something like this](https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html):"
|
|
msgstr "예를 들어 `zerocopy` 크레이트에는 [안전하지 않은 트레잇](https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html)이 있습니다:"
|
|
|
|
#: src/unsafe/unsafe-traits.md:9
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::mem::size_of_val;\n"
|
|
"use std::slice;\n"
|
|
"\n"
|
|
"/// ...\n"
|
|
"/// # Safety\n"
|
|
"/// The type must have a defined representation and no padding.\n"
|
|
"pub unsafe trait AsBytes {\n"
|
|
" fn as_bytes(&self) -> &[u8] {\n"
|
|
" unsafe {\n"
|
|
" slice::from_raw_parts(self as *const Self as *const u8, size_of_val(self))\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// Safe because u32 has a defined representation and no padding.\n"
|
|
"unsafe impl AsBytes for u32 {}\n"
|
|
"```"
|
|
msgstr ""
|
|
"```rust,editable\n"
|
|
"use std::mem::size_of_val;\n"
|
|
"use std::slice;\n"
|
|
"\n"
|
|
"/// ...\n"
|
|
"/// # Safety\n"
|
|
"/// The type must have a defined representation and no padding.\n"
|
|
"pub unsafe trait AsBytes {\n"
|
|
" fn as_bytes(&self) -> &[u8] {\n"
|
|
" unsafe {\n"
|
|
" slice::from_raw_parts(self as *const Self as *const u8, size_of_val(self))\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// Safe because u32 has a defined representation and no padding.\n"
|
|
"unsafe impl AsBytes for u32 {}\n"
|
|
"```"
|
|
|
|
#: src/unsafe/unsafe-traits.md:30
|
|
msgid ""
|
|
"There should be a `# Safety` section on the Rustdoc for the trait explaining the requirements for\n"
|
|
"the trait to be safely implemented."
|
|
msgstr "안전하지 않은 트레잇을 만들 때에는 주석에 `# Safety` 항목이 있어서 이 트레잇을 안전하게 구현하려면 어떤 요구사항들을 만족해야 하는지를 설명해야 합니다."
|
|
|
|
#: src/unsafe/unsafe-traits.md:33
|
|
msgid "The actual safety section for `AsBytes` is rather longer and more complicated."
|
|
msgstr "`AsBytes`에서 지켜야 할 안전성에 대한 실제 설명은 좀 더 길고 복잡합니다."
|
|
|
|
#: src/unsafe/unsafe-traits.md:35
|
|
msgid "The built-in `Send` and `Sync` traits are unsafe."
|
|
msgstr "빌트인 트레잇인 `Send`와 `Sync`는 안전하지 않은 트레잇 입니다."
|
|
|
|
#: src/exercises/day-3/afternoon.md:1
|
|
msgid "# Day 3: Afternoon Exercises"
|
|
msgstr "# 3일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-3/afternoon.md:3
|
|
msgid "Let us build a safe wrapper for reading directory content!"
|
|
msgstr "디렉터리의 내용을 읽는 안전한 래퍼 코드를 작성해 봅시다!"
|
|
|
|
#: src/exercises/day-3/afternoon.md:7
|
|
msgid "After looking at the exercise, you can look at the [solution] provided."
|
|
msgstr "연습문제를 살펴 본 후, 제공된 [해답][solution]을 살펴볼 수 있습니다."
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:1
|
|
msgid "# Safe FFI Wrapper"
|
|
msgstr "# FFI래퍼"
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:3
|
|
msgid ""
|
|
"Rust has great support for calling functions through a _foreign function\n"
|
|
"interface_ (FFI). We will use this to build a safe wrapper for the `libc`\n"
|
|
"functions you would use from C to read the filenames of a directory."
|
|
msgstr "러스트는 _외부 기능 호출(FFI)_을 지원합니다. 우리는 이를 이용하여 디렉터리에서 파일 이름을 읽어오는 `libc` 함수에 대한 안전한 래퍼를 만들 것입니다."
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:7
|
|
msgid "You will want to consult the manual pages:"
|
|
msgstr "아래 리눅스 메뉴얼 문서들을 참조하시기 바랍니다:"
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:9
|
|
msgid ""
|
|
"* [`opendir(3)`](https://man7.org/linux/man-pages/man3/opendir.3.html)\n"
|
|
"* [`readdir(3)`](https://man7.org/linux/man-pages/man3/readdir.3.html)\n"
|
|
"* [`closedir(3)`](https://man7.org/linux/man-pages/man3/closedir.3.html)"
|
|
msgstr ""
|
|
"* [`opendir(3)`](https://man7.org/linux/man-pages/man3/opendir.3.html)\n"
|
|
"* [`readdir(3)`](https://man7.org/linux/man-pages/man3/readdir.3.html)\n"
|
|
"* [`closedir(3)`](https://man7.org/linux/man-pages/man3/closedir.3.html)"
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:13
|
|
msgid ""
|
|
"You will also want to browse the [`std::ffi`] module, particular for [`CStr`]\n"
|
|
"and [`CString`] types which are used to hold NUL-terminated strings coming from\n"
|
|
"C. The [Nomicon] also has a very useful chapter about FFI."
|
|
msgstr "[`std::ffi`] 모듈을 탐색하는게 필요할 지도 모르겠습니다. C에서 사용되는 NUL-terminated 문자열을 담기 위한 [`CStr`]과 [`CString`] 타입을 확인해 보세요. [Nomicon]문서 또한 FFI에 대한 유용한 내용을 담고 있습니다."
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:22
|
|
msgid ""
|
|
"Copy the code below to <https://play.rust-lang.org/> and fill in the missing\n"
|
|
"functions and methods:"
|
|
msgstr "아래 코드를 <https://play.rust-lang.org/>에 복사하고 빠진 함수와 메서드를 채워봅니다:"
|
|
|
|
#: src/exercises/day-3/safe-ffi-wrapper.md:25
|
|
msgid ""
|
|
"```rust,should_panic\n"
|
|
"// TODO: remove this when you're done with your implementation.\n"
|
|
"#![allow(unused_imports, unused_variables, dead_code)]\n"
|
|
"\n"
|
|
"mod ffi {\n"
|
|
" use std::os::raw::{c_char, c_int, c_long, c_ulong, c_ushort};\n"
|
|
"\n"
|
|
" // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.\n"
|
|
" #[repr(C)]\n"
|
|
" pub struct DIR {\n"
|
|
" _data: [u8; 0],\n"
|
|
" _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n"
|
|
" }\n"
|
|
"\n"
|
|
" // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-gnu.\n"
|
|
" #[repr(C)]\n"
|
|
" pub struct dirent {\n"
|
|
" pub d_ino: c_long,\n"
|
|
" pub d_off: c_ulong,\n"
|
|
" pub d_reclen: c_ushort,\n"
|
|
" pub d_type: c_char,\n"
|
|
" pub d_name: [c_char; 256],\n"
|
|
" }\n"
|
|
"\n"
|
|
" extern \"C\" {\n"
|
|
" pub fn opendir(s: *const c_char) -> *mut DIR;\n"
|
|
" pub fn readdir(s: *mut DIR) -> *const dirent;\n"
|
|
" pub fn closedir(s: *mut DIR) -> c_int;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"use std::ffi::{CStr, CString, OsStr, OsString};\n"
|
|
"use std::os::unix::ffi::OsStrExt;\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct DirectoryIterator {\n"
|
|
" path: CString,\n"
|
|
" dir: *mut ffi::DIR,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl DirectoryIterator {\n"
|
|
" fn new(path: &str) -> Result<DirectoryIterator, String> {\n"
|
|
" // Call opendir and return a Ok value if that worked,\n"
|
|
" // otherwise return Err with a message.\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Iterator for DirectoryIterator {\n"
|
|
" type Item = OsString;\n"
|
|
" fn next(&mut self) -> Option<OsString> {\n"
|
|
" // Keep calling readdir until we get a NULL pointer back.\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Drop for DirectoryIterator {\n"
|
|
" fn drop(&mut self) {\n"
|
|
" // Call closedir as needed.\n"
|
|
" unimplemented!()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() -> Result<(), String> {\n"
|
|
" let iter = DirectoryIterator::new(\".\")?;\n"
|
|
" println!(\"files: {:#?}\", iter.collect::<Vec<_>>());\n"
|
|
" Ok(())\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/welcome-day-4.md:1
|
|
msgid "# Welcome to Day 4"
|
|
msgstr "# 4일차 개요"
|
|
|
|
#: src/welcome-day-4.md:3
|
|
msgid ""
|
|
"This morning, we will focus on Concurrency: threads, channels, shared state, `Send` and `Sync`.\n"
|
|
"In the afternoon, we will have a chance to see Rust in action."
|
|
msgstr ""
|
|
"오전에는 동시성을 다룹니다: 쓰레드, 채널, 공유 상태, `Send`와 `Sync`.\n"
|
|
"오후에는 러스트가 실제 사용되는 사례를 살펴봅니다."
|
|
|
|
#: src/welcome-day-4.md:8
|
|
msgid ""
|
|
"This is a good time to give an outline of what you will cover in the afternoon\n"
|
|
"section, as announced in the course offering."
|
|
msgstr "오후에 다룰 내용의 개요를 소개하는 것도 좋습니다."
|
|
|
|
#: src/concurrency.md:1
|
|
msgid "# Fearless Concurrency"
|
|
msgstr "# 겂없는 동시성"
|
|
|
|
#: src/concurrency.md:3
|
|
msgid ""
|
|
"Rust has full support for concurrency using OS threads with mutexes and\n"
|
|
"channels."
|
|
msgstr "러스트는 동시성 지원이 막강합니다. 운영체제 레벨의 스레드를 사용하며, 뮤택스와 채널도 지원합니다."
|
|
|
|
#: src/concurrency.md:6
|
|
msgid ""
|
|
"The Rust type system plays an important role in making many concurrency bugs\n"
|
|
"compile time bugs. This is often referred to as _fearless concurrency_ since you\n"
|
|
"can rely on the compiler to ensure correctness at runtime."
|
|
msgstr "러스트의 타입 시스템은 프로그램에 동시성 버그가 있을 경우, 컴파일 시에 에러가 발생하도록 해 줍니다. 컴파일러를 이용해서 프로그램이 수행시에 정확히 동작함을 미리 보장해 주기 때문에, 사람들은 이를 종종 _겁없는 동시성_ 이라고 합니다."
|
|
|
|
#: src/concurrency/threads.md:1
|
|
msgid "# Threads"
|
|
msgstr "# 스레드"
|
|
|
|
#: src/concurrency/threads.md:3
|
|
msgid "Rust threads work similarly to threads in other languages:"
|
|
msgstr "러스트의 스레드는 다른 언어의 스레드와 유사하게 동작합니다:"
|
|
|
|
#: src/concurrency/threads.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::thread;\n"
|
|
"use std::time::Duration;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" thread::spawn(|| {\n"
|
|
" for i in 1..10 {\n"
|
|
" println!(\"Count in thread: {i}!\");\n"
|
|
" thread::sleep(Duration::from_millis(5));\n"
|
|
" }\n"
|
|
" });\n"
|
|
"\n"
|
|
" for i in 1..5 {\n"
|
|
" println!(\"Main thread: {i}\");\n"
|
|
" thread::sleep(Duration::from_millis(5));\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/threads.md:24
|
|
msgid ""
|
|
"* Threads are all daemon threads, the main thread does not wait for them.\n"
|
|
"* Thread panics are independent of each other.\n"
|
|
" * Panics can carry a payload, which can be unpacked with `downcast_ref`."
|
|
msgstr ""
|
|
"* 스레드는 모두 데몬 스레드입니다. 따라서 메인 스레드는 이 스레드들이 끝나기를 기다리지 않습니다.\n"
|
|
"* 한 스레드에서 발생한 패닉은 다른 스레드에게 영향을 끼치지 않습니다.\n"
|
|
" * 패닉은 추가정보(페이로드)를 포함할 수 있으며, 이는 `downcast_ref`로 풀어볼 수 있습니다."
|
|
|
|
#: src/concurrency/threads.md:32
|
|
msgid ""
|
|
"* Notice that the thread is stopped before it reaches 10 — the main thread is\n"
|
|
" not waiting.\n"
|
|
"\n"
|
|
"* Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for\n"
|
|
" the thread to finish.\n"
|
|
"\n"
|
|
"* Trigger a panic in the thread, notice how this doesn't affect `main`.\n"
|
|
"\n"
|
|
"* Use the `Result` return value from `handle.join()` to get access to the panic\n"
|
|
" payload. This is a good time to talk about [`Any`]."
|
|
msgstr ""
|
|
"* 메인 스레드가 자식 스레드를 기다리지 않기 때문에 자식 스레드의 for문은 10까지 가지 않습니다.\n"
|
|
"\n"
|
|
"* 만약 메인 스레드가 자식 스레드가 끝날 때 까지 기다리기를 원한다면 `let handle = thread::spawn(...)`으로 스레드를 선언한 후 `handle.join()`로 연결하여 사용합니다.\n"
|
|
"\n"
|
|
"* 자식 스레드에서 발생한 패닉이 메인 스레드에는 영향을 주지 않음을 확인하시기 바랍니다.\n"
|
|
"\n"
|
|
"* `handle.join()`사용시 `Result` 반환값을 통해 패닉의 추가정보에 접근할 수 있습니다. 이 시점에서 [`Any`]에 대해 이야기를 해 보면 좋습니다."
|
|
|
|
#: src/concurrency/scoped-threads.md:1
|
|
msgid "# Scoped Threads"
|
|
msgstr "# 범위 스레드(Scoped Threads)"
|
|
|
|
#: src/concurrency/scoped-threads.md:3
|
|
msgid "Normal threads cannot borrow from their environment:"
|
|
msgstr "보통, 스레드는 스레드 밖에서 데이터를 빌릴 수 없습니다:"
|
|
|
|
#: src/concurrency/scoped-threads.md:5
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::thread;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let s = String::from(\"Hello\");\n"
|
|
"\n"
|
|
" thread::spawn(|| {\n"
|
|
" println!(\"Length: {}\", s.len());\n"
|
|
" });\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/scoped-threads.md:17
|
|
msgid "However, you can use a [scoped thread][1] for this:"
|
|
msgstr "하지만, [scoped thread][1]에서는 가능합니다:"
|
|
|
|
#: src/concurrency/scoped-threads.md:19
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::thread;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let s = String::from(\"Hello\");\n"
|
|
"\n"
|
|
" thread::scope(|scope| {\n"
|
|
" scope.spawn(|| {\n"
|
|
" println!(\"Length: {}\", s.len());\n"
|
|
" });\n"
|
|
" });\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/scoped-threads.md:37
|
|
msgid ""
|
|
"* The reason for that is that when the `thread::scope` function completes, all the threads are guaranteed to be joined, so they can return borrowed data.\n"
|
|
"* Normal Rust borrowing rules apply: you can either borrow mutably by one thread, or immutably by any number of threads.\n"
|
|
" "
|
|
msgstr ""
|
|
"* `thread::scope` 함수가 완료되면 그 안에서 생성된 모든 스레드들이 종료했음이 보장되기 때문에, 그 때 빌렸던 데이터들을 다시 반환할 수 있기 때문입니다.\n"
|
|
"* 일반적인 러스트의 빌림 규칙이 적용됩니다: 한 스레드에 의한 가변 빌림 또는 여러 스레드에 대한 불변 빌림중 하나만 가능합니다.\n"
|
|
" "
|
|
|
|
#: src/concurrency/channels.md:1
|
|
msgid "# Channels"
|
|
msgstr "# 채널"
|
|
|
|
#: src/concurrency/channels.md:3
|
|
msgid ""
|
|
"Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts\n"
|
|
"are connected via the channel, but you only see the end-points."
|
|
msgstr "러스트의 채널은 `Sender<T>` 와 `Receiver<T>` 두 부분으로 구성됩니다. 이 둘은 채널을 통해 서로 연결되어 있지만, 우리는 채널을 볼 수는 없고 이 양 끝단만을 사용하게 됩니다."
|
|
|
|
#: src/concurrency/channels.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::sync::mpsc;\n"
|
|
"use std::thread;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let (tx, rx) = mpsc::channel();\n"
|
|
"\n"
|
|
" tx.send(10).unwrap();\n"
|
|
" tx.send(20).unwrap();\n"
|
|
"\n"
|
|
" println!(\"Received: {:?}\", rx.recv());\n"
|
|
" println!(\"Received: {:?}\", rx.recv());\n"
|
|
"\n"
|
|
" let tx2 = tx.clone();\n"
|
|
" tx2.send(30).unwrap();\n"
|
|
" println!(\"Received: {:?}\", rx.recv());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/channels.md:27
|
|
msgid ""
|
|
"* `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender` implement `Clone` (so\n"
|
|
" you can make multiple producers) but `Receiver` does not.\n"
|
|
"* `send()` and `recv()` return `Result`. If they return `Err`, it means the counterpart `Sender` or\n"
|
|
" `Receiver` is dropped and the channel is closed."
|
|
msgstr ""
|
|
"* `mpsc`는 “Multi-Produce, Single-Consumer”를 의미합니다. `Sender`와 `SyncSender`는 `Clone`을 구현하지만 (즉, 여러개의 producer를 만들수 있습니다) `Receiver`는 `Clone`을 구현하지 않습니다.\n"
|
|
"* `send()`와 `recv()`는 `Result`를 반환합니다. 만일 `Err`가 반환된다면, 상대방의 `Sender`또는 `Receiver`가 삭제되었고 채널이 닫혔다는 뜻입니다."
|
|
|
|
#: src/concurrency/channels/unbounded.md:1
|
|
msgid "# Unbounded Channels"
|
|
msgstr "# 무경계 채널(Unbounded Channels)"
|
|
|
|
#: src/concurrency/channels/unbounded.md:3
|
|
msgid "You get an unbounded and asynchronous channel with `mpsc::channel()`:"
|
|
msgstr "`mpsc::channel()` 함수는 경계가 없는 비동기 채널을 생성합니다:"
|
|
|
|
#: src/concurrency/channels/unbounded.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::sync::mpsc;\n"
|
|
"use std::thread;\n"
|
|
"use std::time::Duration;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let (tx, rx) = mpsc::channel();\n"
|
|
"\n"
|
|
" thread::spawn(move || {\n"
|
|
" let thread_id = thread::current().id();\n"
|
|
" for i in 1..10 {\n"
|
|
" tx.send(format!(\"Message {i}\")).unwrap();\n"
|
|
" println!(\"{thread_id:?}: sent Message {i}\");\n"
|
|
" }\n"
|
|
" println!(\"{thread_id:?}: done\");\n"
|
|
" });\n"
|
|
" thread::sleep(Duration::from_millis(100));\n"
|
|
"\n"
|
|
" for msg in rx.iter() {\n"
|
|
" println!(\"Main: got {}\", msg);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/channels/bounded.md:1
|
|
msgid "# Bounded Channels"
|
|
msgstr "# 경계가 있는 채널(Bounded Channels)"
|
|
|
|
#: src/concurrency/channels/bounded.md:3
|
|
msgid "Bounded and synchronous channels make `send` block the current thread:"
|
|
msgstr "경계가 있는 동기 채널은 `send`가 현제 스레드를 블로킹 하도록 만듭니다:"
|
|
|
|
#: src/concurrency/channels/bounded.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::sync::mpsc;\n"
|
|
"use std::thread;\n"
|
|
"use std::time::Duration;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let (tx, rx) = mpsc::sync_channel(3);\n"
|
|
"\n"
|
|
" thread::spawn(move || {\n"
|
|
" let thread_id = thread::current().id();\n"
|
|
" for i in 1..10 {\n"
|
|
" tx.send(format!(\"Message {i}\")).unwrap();\n"
|
|
" println!(\"{thread_id:?}: sent Message {i}\");\n"
|
|
" }\n"
|
|
" println!(\"{thread_id:?}: done\");\n"
|
|
" });\n"
|
|
" thread::sleep(Duration::from_millis(100));\n"
|
|
"\n"
|
|
" for msg in rx.iter() {\n"
|
|
" println!(\"Main: got {msg}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state.md:1
|
|
msgid "# Shared State"
|
|
msgstr "# 상태 공유(Shared State)"
|
|
|
|
#: src/concurrency/shared_state.md:3
|
|
msgid ""
|
|
"Rust uses the type system to enforce synchronization of shared data. This is\n"
|
|
"primarily done via two types:"
|
|
msgstr "러스트는 주로 아래 두 가지 타입을 이용해서 공유 데이터 동기화를 수행합니다:"
|
|
|
|
#: src/concurrency/shared_state.md:6
|
|
msgid ""
|
|
"* [`Arc<T>`][1], atomic reference counted `T`: handles sharing between threads and\n"
|
|
" takes care to deallocate `T` when the last reference is dropped,\n"
|
|
"* [`Mutex<T>`][2]: ensures mutually exclusive access to the `T` value."
|
|
msgstr ""
|
|
"* [`Arc<T>`][1], `T`에 대한 아토믹 참조 카운트: 이 참조는 다수의 스레드 사이에서 공유될 수 있고, 참조하던 마지막 스레드가 종료할 경우 `T`를 반환합니다.\n"
|
|
"* [`Mutex<T>`][2]: `T`값에 대한 상호 배제 엑세스를 보장합니다."
|
|
|
|
#: src/concurrency/shared_state/arc.md:1
|
|
msgid "# `Arc`"
|
|
msgstr "# `Arc`"
|
|
|
|
#: src/concurrency/shared_state/arc.md:3
|
|
msgid "[`Arc<T>`][1] allows shared read-only access via its `clone` method:"
|
|
msgstr "[`Arc<T>`][1]의 `clone` 메서드를 이용하면 여러 스레드가 한 데이터를 동시에 (_역주_: 그리고 안전하게. 스레드가 하나라도 살아있는 동안에는 `T`가 반환되지 않음) 읽을 수 있습니다:"
|
|
|
|
#: src/concurrency/shared_state/arc.md:5
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::thread;\n"
|
|
"use std::sync::Arc;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let v = Arc::new(vec![10, 20, 30]);\n"
|
|
" let mut handles = Vec::new();\n"
|
|
" for _ in 1..5 {\n"
|
|
" let v = v.clone();\n"
|
|
" handles.push(thread::spawn(move || {\n"
|
|
" let thread_id = thread::current().id();\n"
|
|
" println!(\"{thread_id:?}: {v:?}\");\n"
|
|
" }));\n"
|
|
" }\n"
|
|
"\n"
|
|
" handles.into_iter().for_each(|h| h.join().unwrap());\n"
|
|
" println!(\"v: {v:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state/arc.md:29
|
|
msgid ""
|
|
"* `Arc` stands for \"Atomic Reference Counted\", a thread safe version of `Rc` that uses atomic\n"
|
|
" operations.\n"
|
|
"* `Arc<T>` implements `Clone` whether or not `T` does. It implements `Send` and `Sync` iff `T`\n"
|
|
" implements them both.\n"
|
|
"* `Arc::clone()` has the cost of atomic operations that get executed, but after that the use of the\n"
|
|
" `T` is free.\n"
|
|
"* Beware of reference cycles, `Arc` does not use a garbage collector to detect them.\n"
|
|
" * `std::sync::Weak` can help."
|
|
msgstr ""
|
|
"* `Arc`는 \"Atomic Reference Counted\"를 의미하며, 스레드 안정성을 보장하는 `Rc`라고 생각하면 됩니다.\n"
|
|
"* `T`가 `Clone`을 구현하든 안하든 `Arc<T>`는 `Clone`을 구현합니다. `Send`와 `Sync`는 `T`가 이들을 구현하는 경우에만 구현됩니다.\n"
|
|
"* `Arc::clone()`는 아토믹 연산을 수행하기 때문에 그 때 코스트가 좀 있지만, 일단 `clone()`이 끝난 후 `T`를 사용하는 대에는 아무런 오버헤드가 없습니다.\n"
|
|
"* 순환 참조가 발생하지 않도록 주의해야 합니다. 러스트는 순환 참조를 감지하는 가비지 컬랙터가 없습니다.\n"
|
|
" * 순환 참조를 피하는데 `std::sync::Weak`가 도움이 될 것입니다."
|
|
|
|
#: src/concurrency/shared_state/mutex.md:1
|
|
msgid "# `Mutex`"
|
|
msgstr "# `Mutex`"
|
|
|
|
#: src/concurrency/shared_state/mutex.md:3
|
|
msgid ""
|
|
"[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`\n"
|
|
"behind a read-only interface:"
|
|
msgstr "[`Mutex<T>`][1]를 이용하면 불변 참조를 통해서도 `T`의 값을 수정할 수가 있으며, _이에 더해서_ 한 번에 한 스레드만 `T`의 값을 접근(읽거나 쓰거나)함을 보장해 줍니다:"
|
|
|
|
#: src/concurrency/shared_state/mutex.md:6
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::sync::Mutex;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let v = Mutex::new(vec![10, 20, 30]);\n"
|
|
" println!(\"v: {:?}\", v.lock().unwrap());\n"
|
|
"\n"
|
|
" {\n"
|
|
" let mut guard = v.lock().unwrap();\n"
|
|
" guard.push(40);\n"
|
|
" }\n"
|
|
"\n"
|
|
" println!(\"v: {:?}\", v.lock().unwrap());\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state/mutex.md:22
|
|
msgid ""
|
|
"Notice how we have a [`impl<T: Send> Sync for Mutex<T>`][2] blanket\n"
|
|
"implementation."
|
|
msgstr "모든 `Mutex<T>`는 [`impl<T: Send> Sync for Mutex<T>`][2]를 자동으로 구현함을 참조하세요."
|
|
|
|
#: src/concurrency/shared_state/mutex.md:31
|
|
msgid ""
|
|
"* `Mutex` in Rust looks like a collection with just one element - the protected data.\n"
|
|
" * It is not possible to forget to acquire the mutex before accessing the protected data.\n"
|
|
"* You can get an `&mut T` from an `&Mutex<T>` by taking the lock. The `MutexGuard` ensures that the\n"
|
|
" `&mut T` doesn't outlive the lock being held.\n"
|
|
"* `Mutex<T>` implements both `Send` and `Sync` iff `T` implements `Send`.\n"
|
|
"* A read-write lock counterpart - `RwLock`.\n"
|
|
"* Why does `lock()` return a `Result`? \n"
|
|
" * If the thread that held the `Mutex` panicked, the `Mutex` becomes \"poisoned\" to signal that\n"
|
|
" the data it protected might be in an inconsistent state. Calling `lock()` on a poisoned mutex\n"
|
|
" fails with a [`PoisonError`]. You can call `into_inner()` on the error to recover the data\n"
|
|
" regardless."
|
|
msgstr ""
|
|
"* 러스트의 `Mutex`는 오직 하나의 데이터만 담을 수 있는 컬렉션처럼 볼 수도 있습니다. 다른 컬렉션과 다른 점은, 그 데이터가 동시성 문제로부터 자유롭다는 점입니다.\n"
|
|
" * `Mutex`는 뮤텍스를 획득하지 않으면 보호된 데이터에 접근하는 것이 불가능 하도록 디자인 되어 있습니다.\n"
|
|
"* `&Mutex<T>`에 대해 lock을 획득하면 `&mut T`를 얻을 수 있습니다. `MutexGuard`는 `&mut T`가 획득한 lock보다 오래 살아남지 않음을 보장합니다.\n"
|
|
"* `Mutex<T>`는 오직 `T`가 `Send`를 구현하는 경우에만 `Send`와 `Sync`를 구현합니다.\n"
|
|
"* 읽기-쓰기 lock은 `RwLock`을 사용합니다.\n"
|
|
"* 왜 `lock()`이 `Result`를 반환할까요?\n"
|
|
" * `Mutex`를 획득한 스레드에서 패닉이 발생하면, 데이터가 올바르지 않은 상황이 될 수 있습니다. 이를 `Mutex`가 \"중독(poisoned)\" 되었다고 표현하며, 중독된 뮤텍스에서 `lock()`을 호출하면 실패하고 [`PoisonError`]가 발생합니다. 이러한 오류로부터 데이터를 복구하기 위해 `into_inner()`를 호출할 수 있습니다."
|
|
|
|
#: src/concurrency/shared_state/example.md:3
|
|
msgid "Let us see `Arc` and `Mutex` in action:"
|
|
msgstr "`Arc`와 `Mutex`의 동작을 살펴봅시다:"
|
|
|
|
#: src/concurrency/shared_state/example.md:5
|
|
msgid ""
|
|
"```rust,editable,compile_fail\n"
|
|
"use std::thread;\n"
|
|
"// use std::sync::{Arc, Mutex};\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let mut v = vec![10, 20, 30];\n"
|
|
" let handle = thread::spawn(|| {\n"
|
|
" v.push(10);\n"
|
|
" });\n"
|
|
" v.push(1000);\n"
|
|
"\n"
|
|
" handle.join().unwrap();\n"
|
|
" println!(\"v: {v:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state/example.md:23
|
|
msgid "Possible solution:"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state/example.md:25
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"use std::sync::{Arc, Mutex};\n"
|
|
"use std::thread;\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let v = Arc::new(Mutex::new(vec![10, 20, 30]));\n"
|
|
"\n"
|
|
" let v2 = v.clone();\n"
|
|
" let handle = thread::spawn(move || {\n"
|
|
" let mut v2 = v2.lock().unwrap();\n"
|
|
" v2.push(10);\n"
|
|
" });\n"
|
|
"\n"
|
|
" {\n"
|
|
" let mut v = v.lock().unwrap();\n"
|
|
" v.push(1000);\n"
|
|
" }\n"
|
|
"\n"
|
|
" handle.join().unwrap();\n"
|
|
"\n"
|
|
" println!(\"v: {v:?}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/shared_state/example.md:49
|
|
msgid "Notable parts:"
|
|
msgstr "눈여겨 볼 부분:"
|
|
|
|
#: src/concurrency/shared_state/example.md:51
|
|
msgid ""
|
|
"* `v` is wrapped in both `Arc` and `Mutex`, because their concerns are orthogonal.\n"
|
|
" * Wrapping a `Mutex` in an `Arc` is a common pattern to share mutable state between threads.\n"
|
|
"* `v: Arc<_>` needs to be cloned as `v2` before it can be moved into another thread. Note `move` was added to the lambda signature.\n"
|
|
"* Blocks are introduced to narrow the scope of the `LockGuard` as much as possible."
|
|
msgstr ""
|
|
"* `v`는 `Arc`와 `Mutex` 모두에 포함되어 있습니다. 이는 `Arc`와 `Mutex`가 서로 완전히 다른 문제를 위한 도구이기 때문입니다.\n"
|
|
" * `Mutex`를 `Arc`로 래핑하는 것은 가변 상태를 스레드들 간에 공유할 때 흔히 사용하는 패턴입니다.\n"
|
|
"* `v: Arc<_>`를 다른 스레드에서 사용하려면, 먼저 `v2`로 복사를 하고 이를 그 스레드로 이동 해야 합니다. 그래서 람다의 시그니처에 `move`가 있는 것입니다.\n"
|
|
"* 블록은 `LockGuard`의 범위를 최대한 좁히기 위해 사용되었습니다."
|
|
|
|
#: src/concurrency/send-sync.md:1
|
|
msgid "# `Send` and `Sync`"
|
|
msgstr "# `Send`와 `Sync`"
|
|
|
|
#: src/concurrency/send-sync.md:3
|
|
msgid "How does Rust know to forbid shared access across thread? The answer is in two traits:"
|
|
msgstr "러스트는 어떻게 해서 스레드 간에 특정 데이터 타입이 공유될 수 있거나, 안된다는 것을 알까요? 정답은 다음 두 트레잇에 있습니다:"
|
|
|
|
#: src/concurrency/send-sync.md:5
|
|
msgid ""
|
|
"* [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread\n"
|
|
" boundary.\n"
|
|
"* [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread\n"
|
|
" boundary."
|
|
msgstr ""
|
|
"* [`Send`][1]: `T`가 스레드 간 이동이 안전하다면, `T`의 타입은 `Send`입니다.\n"
|
|
"* [`Sync`][2]: `&T`가 스레드 간 이동이 안전하다면, `&T`의 타입은 `Sync`입니다."
|
|
|
|
#: src/concurrency/send-sync.md:10
|
|
msgid ""
|
|
"`Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive them for your types\n"
|
|
"as long as they only contain `Send` and `Sync` types. You can also implement them manually when you\n"
|
|
"know it is valid."
|
|
msgstr "`Send`와 `Sync` 트레잇은 [안전하지 않은 트레잇][3]입니다. 컴파일러는 타입의 요소들이 모두 `Send`와 `Sync` 타입이면 자동으로 이 트레잇들을 적용시켜 줍니다. 물론 여러분 스스로 맞다고 알고 있다면 직접 구현해도 됩니다."
|
|
|
|
#: src/concurrency/send-sync.md:20
|
|
msgid ""
|
|
"* One can think of these traits as markers that the type has certain thread-safety properties.\n"
|
|
"* They can be used in the generic constraints as normal traits.\n"
|
|
" "
|
|
msgstr ""
|
|
"* `Sync`와 `Send`는 어떤 타입이 특정한 스레드-안전 속성을 가짐을 나타내는 마커로 생각할 수 있습니다.\n"
|
|
"* 이 두 트레이트는 제너릭에서 제약 조건을 나타내는 트레이트로 사용될 수도 있습니다.\n"
|
|
" "
|
|
|
|
#: src/concurrency/send-sync/send.md:1
|
|
msgid "# `Send`"
|
|
msgstr "# `Send`"
|
|
|
|
#: src/concurrency/send-sync/send.md:3
|
|
msgid "> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread."
|
|
msgstr "> `T`가 스레드 간에 안전하게 이동될 수 있다면, `T`의 타입은 `Send`입니다."
|
|
|
|
#: src/concurrency/send-sync/send.md:5
|
|
msgid ""
|
|
"The effect of moving ownership to another thread is that _destructors_ will run\n"
|
|
"in that thread. So the question is when you can allocate a value in one thread\n"
|
|
"and deallocate it in another."
|
|
msgstr "소유권을 다른 스레드로 이동하면 소멸자가 해당 스레드에서 실행됩니다. 여기서 의문은 \"언제 한 스레드에서 값을 할당하고 다른 스레드에서 값을 할당 해제할 수 있는가\" 입니다."
|
|
|
|
#: src/concurrency/send-sync/sync.md:1
|
|
msgid "# `Sync`"
|
|
msgstr "# `Sync`"
|
|
|
|
#: src/concurrency/send-sync/sync.md:3
|
|
msgid ""
|
|
"> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple\n"
|
|
"> threads at the same time."
|
|
msgstr "> `&T`가 여러 스레드에서 안전하게 접근될 수 있다면, `&T`의 타입은 `Sync`입니다."
|
|
|
|
#: src/concurrency/send-sync/sync.md:6
|
|
msgid "More precisely, the definition is:"
|
|
msgstr "좀 더 정확한 정의는 다음과 같습니다:"
|
|
|
|
#: src/concurrency/send-sync/sync.md:8
|
|
msgid "> `T` is `Sync` if and only if `&T` is `Send`"
|
|
msgstr "`&T`가 `Send`인 경우에만 `T`의 타입이 `Sync`가 됩니다"
|
|
|
|
#: src/concurrency/send-sync/sync.md:14
|
|
msgid "This statement is essentially a shorthand way of saying that if a type is thread-safe for shared use, it is also thread-safe to pass references of it across threads."
|
|
msgstr "위 문장을 풀어서 이야기 하면, 어떤 타입이 스레드 간에 공유되어서 사용되기에 안전하다면 그 타입의 참조 타입은 스레드 간에 이동 가능하다는 것입니다."
|
|
|
|
#: src/concurrency/send-sync/sync.md:16
|
|
msgid "This is because if a type is Sync it means that it can be shared across multiple threads without the risk of data races or other synchronization issues, so it is safe to move it to another thread. A reference to the type is also safe to move to another thread, because the data it references can be accessed from any thread safely."
|
|
msgstr "이는 다음과 같이 증명할 수 있습니다: 어떤 타입이 `Sync`라는 말은 곧 그 타입이 여러 스레드들 사이에서 데이터 레이스나 여타 동기화 문제 없이 공유 가능하다는 말입니다. 스레드 간 공유가 안전하다면, 스레드간 이동도 안전할 수 밖에 없습니다. 어떤 타입의 스레드간 이동이 안전하다면, 그 타입의 참조 또한 스레드간 이동이 안전할 수 밖에 없습니다."
|
|
|
|
#: src/concurrency/send-sync/examples.md:1
|
|
msgid "# Examples"
|
|
msgstr "# 예제"
|
|
|
|
#: src/concurrency/send-sync/examples.md:3
|
|
msgid "## `Send + Sync`"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/send-sync/examples.md:5
|
|
msgid "Most types you come across are `Send + Sync`:"
|
|
msgstr "여러분이 다루게 될 대부분의 타입은 `Send + Sync`입니다:"
|
|
|
|
#: src/concurrency/send-sync/examples.md:7
|
|
msgid ""
|
|
"* `i8`, `f32`, `bool`, `char`, `&str`, ...\n"
|
|
"* `(T1, T2)`, `[T; N]`, `&[T]`, `struct { x: T }`, ...\n"
|
|
"* `String`, `Option<T>`, `Vec<T>`, `Box<T>`, ...\n"
|
|
"* `Arc<T>`: Explicitly thread-safe via atomic reference count.\n"
|
|
"* `Mutex<T>`: Explicitly thread-safe via internal locking.\n"
|
|
"* `AtomicBool`, `AtomicU8`, ...: Uses special atomic instructions."
|
|
msgstr ""
|
|
"* `i8`, `f32`, `bool`, `char`, `&str`, ...\n"
|
|
"* `(T1, T2)`, `[T; N]`, `&[T]`, `struct { x: T }`, ...\n"
|
|
"* `String`, `Option<T>`, `Vec<T>`, `Box<T>`, ...\n"
|
|
"* `Arc<T>`: 참조 카운트 조작을 아토믹 하기 때문에 스레드 안전함.\n"
|
|
"* `Mutex<T>`: 값을 접근하기 위해 뮤택스를 잠궈야 하기 때문에 스레드 안전함.\n"
|
|
"* `AtomicBool`, `AtomicU8`, ...: 값을 접근할 때 특별한 아토믹 명령어들을 사용합니다."
|
|
|
|
#: src/concurrency/send-sync/examples.md:14
|
|
msgid ""
|
|
"The generic types are typically `Send + Sync` when the type parameters are\n"
|
|
"`Send + Sync`."
|
|
msgstr "제네릭 타입은 일반적으로 타입 파라메터가 `Send + Sync`이면 `Send + Sync` 입니다."
|
|
|
|
#: src/concurrency/send-sync/examples.md:17
|
|
msgid "## `Send + !Sync`"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/send-sync/examples.md:19
|
|
msgid ""
|
|
"These types can be moved to other threads, but they're not thread-safe.\n"
|
|
"Typically because of interior mutability:"
|
|
msgstr "아래 타입들은 다른 스레드로 이동될 수 있지만 내부적으로 값이 변경될 수 있기 때문에 스레드 안전하지 않습니다:"
|
|
|
|
#: src/concurrency/send-sync/examples.md:22
|
|
msgid ""
|
|
"* `mpsc::Sender<T>`\n"
|
|
"* `mpsc::Receiver<T>`\n"
|
|
"* `Cell<T>`\n"
|
|
"* `RefCell<T>`"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/send-sync/examples.md:27
|
|
msgid "## `!Send + Sync`"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/send-sync/examples.md:29
|
|
msgid "These types are thread-safe, but they cannot be moved to another thread:"
|
|
msgstr "아래 타입들은 스레드 안전하지만 다른 스레드로 이동될 수 없습니다:"
|
|
|
|
#: src/concurrency/send-sync/examples.md:31
|
|
msgid ""
|
|
"* `MutexGuard<T>`: Uses OS level primitives which must be deallocated on the\n"
|
|
" thread which created them."
|
|
msgstr "* `MutexGuard<T>`: 내부적으로, 운영체제가 제공하는 primitive를 사용하는데, 이 primitive는 생성된 스레드에서 해제가 이루어져야 합니다. (_역주_: 그래서 다른 스레드로 옮길 수 없습니다.)"
|
|
|
|
#: src/concurrency/send-sync/examples.md:34
|
|
msgid "## `!Send + !Sync`"
|
|
msgstr ""
|
|
|
|
#: src/concurrency/send-sync/examples.md:36
|
|
msgid "These types are not thread-safe and cannot be moved to other threads:"
|
|
msgstr "아래 타입들은 스레드 안전하지도 않고 다른 스레드로 이동될 수도 없습니다:"
|
|
|
|
#: src/concurrency/send-sync/examples.md:38
|
|
msgid ""
|
|
"* `Rc<T>`: each `Rc<T>` has a reference to an `RcBox<T>`, which contains a\n"
|
|
" non-atomic reference count.\n"
|
|
"* `*const T`, `*mut T`: Rust assumes raw pointers may have special\n"
|
|
" concurrency considerations."
|
|
msgstr ""
|
|
"* `Rc<T>`: `Rc<T>` 는 아토믹하지 않은 방식으로 참조 카운트를 조작하는 `RcBox<T>`를 참조합니다.\n"
|
|
"* `*const T`, `*mut T`: 러스트는 포인터가 스레드 안전하지 않다고 가정합니다."
|
|
|
|
#: src/exercises/day-4/morning.md:1 src/exercises/day-4/android.md:1
|
|
msgid "# Exercises"
|
|
msgstr "# 연습문제"
|
|
|
|
#: src/exercises/day-4/morning.md:3
|
|
msgid "Let us practice our new concurrency skills with"
|
|
msgstr "동시성 기법들을 연습해 봅시다"
|
|
|
|
#: src/exercises/day-4/morning.md:5
|
|
msgid ""
|
|
"* Dining philosophers: a classic problem in concurrency.\n"
|
|
"\n"
|
|
"* Multi-threaded link checker: a larger project where you'll use Cargo to\n"
|
|
" download dependencies and then check links in parallel."
|
|
msgstr ""
|
|
"* 식사하는 철학자 문제: 고적적인 동시성 문제입니다.\n"
|
|
"\n"
|
|
"* 멀티 스레드 링크 검사기: 병렬적으로 웹페이지의 링크들을 체크합니다. 카고를 통해 몇 가지 의존성들을 다운도르 받아야 하는 큰 프로젝트 입니다."
|
|
|
|
#: src/exercises/day-4/dining-philosophers.md:1
|
|
msgid "# Dining Philosophers"
|
|
msgstr "# 식사하는 철학자들"
|
|
|
|
#: src/exercises/day-4/dining-philosophers.md:3
|
|
msgid "The dining philosophers problem is a classic problem in concurrency:"
|
|
msgstr "식사하는 철학자 문제는 동시성에 있어서 고전적인 문제입니다:"
|
|
|
|
#: src/exercises/day-4/dining-philosophers.md:5
|
|
msgid ""
|
|
"> Five philosophers dine together at the same table. Each philosopher has their\n"
|
|
"> own place at the table. There is a fork between each plate. The dish served is\n"
|
|
"> a kind of spaghetti which has to be eaten with two forks. Each philosopher can\n"
|
|
"> only alternately think and eat. Moreover, a philosopher can only eat their\n"
|
|
"> spaghetti when they have both a left and right fork. Thus two forks will only\n"
|
|
"> be available when their two nearest neighbors are thinking, not eating. After\n"
|
|
"> an individual philosopher finishes eating, they will put down both forks."
|
|
msgstr ""
|
|
"> 5명의 철학자가 원탁에서 식사를 하고 있습니다. \n"
|
|
"> 철학자는 원탁에서 자신의 자리에 앉아있습니다.\n"
|
|
"> 포크는 각 접시 사이에 있습니다. \n"
|
|
"> 제공되는 요리를 먹기 위해서는 두 개의 포크를 모두 사용해야합니다.\n"
|
|
"> 철학자는 생각을 하다가 배가 고프면 자신의 좌,우의 포크를 들어 요리를 먹습니다.\n"
|
|
"> 철학자는 요리를 먹은 후에는 포크를 다시 자리에 내려놓습니다.\n"
|
|
"> 철학자는 자신의 좌,우에 포크가 있을때만 요리를 먹을 수 있습니다.\n"
|
|
"> 따라서 두 개의 포크는 오직 자신의 좌,우 철학자가 생각을 할 때만 사용할 수 있습니다."
|
|
|
|
#: src/exercises/day-4/dining-philosophers.md:13
|
|
msgid ""
|
|
"You will need a local [Cargo installation](../../cargo/running-locally.md) for\n"
|
|
"this exercise. Copy the code below to `src/main.rs` file, fill out the blanks,\n"
|
|
"and test that `cargo run` does not deadlock:"
|
|
msgstr "이번 훈련에서는 [카고 설치하기](../../cargo/running-locally.md)가 필요합니다. 아래 코드를 복사해서 `src/main.rs`에 붙여놓고 빈 부분을 채우고, `cargo run` 커맨드로 테스트 해서 교착상태(데드락)가 발생하지 않는지 확인합니다:"
|
|
|
|
#: src/exercises/day-4/dining-philosophers.md:17
|
|
msgid ""
|
|
"```rust,compile_fail\n"
|
|
"use std::sync::mpsc;\n"
|
|
"use std::sync::{Arc, Mutex};\n"
|
|
"use std::thread;\n"
|
|
"use std::time::Duration;\n"
|
|
"\n"
|
|
"struct Fork;\n"
|
|
"\n"
|
|
"struct Philosopher {\n"
|
|
" name: String,\n"
|
|
" // left_fork: ...\n"
|
|
" // right_fork: ...\n"
|
|
" // thoughts: ...\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Philosopher {\n"
|
|
" fn think(&self) {\n"
|
|
" self.thoughts\n"
|
|
" .send(format!(\"Eureka! {} has a new idea!\", &self.name))\n"
|
|
" .unwrap();\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn eat(&self) {\n"
|
|
" // Pick up forks...\n"
|
|
" println!(\"{} is eating...\", &self.name);\n"
|
|
" thread::sleep(Duration::from_millis(10));\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"static PHILOSOPHERS: &[&str] =\n"
|
|
" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" // Create forks\n"
|
|
"\n"
|
|
" // Create philosophers\n"
|
|
"\n"
|
|
" // Make them think and eat\n"
|
|
"\n"
|
|
" // Output their thoughts\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:1
|
|
msgid "# Multi-threaded Link Checker"
|
|
msgstr "# 멀티스레드 링크 검사기"
|
|
|
|
#: src/exercises/day-4/link-checker.md:3
|
|
msgid ""
|
|
"Let us use our new knowledge to create a multi-threaded link checker. It should\n"
|
|
"start at a webpage and check that links on the page are valid. It should\n"
|
|
"recursively check other pages on the same domain and keep doing this until all\n"
|
|
"pages have been validated."
|
|
msgstr "새로 배운것들을 활용해서 멀티 스레드 링크 검사기를 만듭니다. 이 검사기는 웹페이지 안에 있는 링크들이 유효한지 확인합니다. 그리고 재귀적으로 동일 도메인의 다른 모든 페이지가 유효한지 확인합니다."
|
|
|
|
#: src/exercises/day-4/link-checker.md:8
|
|
msgid ""
|
|
"For this, you will need an HTTP client such as [`reqwest`][1]. Create a new\n"
|
|
"Cargo project and `reqwest` it as a dependency with:"
|
|
msgstr "이를 위해서 [`reqwest`][1]와 같은 HTTP 클라이언트가 필요합니다. 새로운 로컬 프로젝트를 만들고 [`reqwest`][1]를 의존성에 추가하십시요:"
|
|
|
|
#: src/exercises/day-4/link-checker.md:11
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ cargo new link-checker\n"
|
|
"$ cd link-checker\n"
|
|
"$ cargo add --features blocking,rustls-tls reqwest\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:17
|
|
msgid ""
|
|
"> If `cargo add` fails with `error: no such subcommand`, then please edit the\n"
|
|
"> `Cargo.toml` file by hand. Add the dependencies listed below."
|
|
msgstr "> 만일 `cargo add` 커맨드가 `error: no such subcommand` 로 실패한다면 `Cargo.toml` 파일을 직접 수정해도 됩니다. 아래에 전체 의존성 내용이 있습니다."
|
|
|
|
#: src/exercises/day-4/link-checker.md:20
|
|
msgid "You will also need a way to find links. We can use [`scraper`][2] for that:"
|
|
msgstr "링크를 찾기 위해서 [`scraper`][2]도 추가합니다:"
|
|
|
|
#: src/exercises/day-4/link-checker.md:22
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ cargo add scraper\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:26
|
|
msgid ""
|
|
"Finally, we'll need some way of handling errors. We use [`thiserror`][3] for\n"
|
|
"that:"
|
|
msgstr "마지막으로 오류 처리하는 방법으로 [`thiserror`][3]도 추가합니다:"
|
|
|
|
#: src/exercises/day-4/link-checker.md:29
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ cargo add thiserror\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:33
|
|
msgid "The `cargo add` calls will update the `Cargo.toml` file to look like this:"
|
|
msgstr "모든 `cargo add`가 끝나면 `Cargo.toml`에 아래 내용이 추가됩니다:"
|
|
|
|
#: src/exercises/day-4/link-checker.md:35
|
|
msgid ""
|
|
"```toml\n"
|
|
"[dependencies]\n"
|
|
"reqwest = { version = \"0.11.12\", features = [\"blocking\", \"rustls-tls\"] }\n"
|
|
"scraper = \"0.13.0\"\n"
|
|
"thiserror = \"1.0.37\"\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:42
|
|
msgid ""
|
|
"You can now download the start page. Try with a small site such as\n"
|
|
"`https://www.google.org/`."
|
|
msgstr "이제 `https://www.google.org/` 같은 웹 페이지를 탐색할 수 있습니다."
|
|
|
|
#: src/exercises/day-4/link-checker.md:45
|
|
msgid "Your `src/main.rs` file should look something like this:"
|
|
msgstr "`rc/main.rs`파일은 아래와 같습니다:"
|
|
|
|
#: src/exercises/day-4/link-checker.md:47
|
|
msgid ""
|
|
"```rust,compile_fail\n"
|
|
"use reqwest::blocking::{get, Response};\n"
|
|
"use reqwest::Url;\n"
|
|
"use scraper::{Html, Selector};\n"
|
|
"use thiserror::Error;\n"
|
|
"\n"
|
|
"#[derive(Error, Debug)]\n"
|
|
"enum Error {\n"
|
|
" #[error(\"request error: {0}\")]\n"
|
|
" ReqwestError(#[from] reqwest::Error),\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn extract_links(response: Response) -> Result<Vec<Url>, Error> {\n"
|
|
" let base_url = response.url().to_owned();\n"
|
|
" let document = response.text()?;\n"
|
|
" let html = Html::parse_document(&document);\n"
|
|
" let selector = Selector::parse(\"a\").unwrap();\n"
|
|
"\n"
|
|
" let mut valid_urls = Vec::new();\n"
|
|
" for element in html.select(&selector) {\n"
|
|
" if let Some(href) = element.value().attr(\"href\") {\n"
|
|
" match base_url.join(href) {\n"
|
|
" Ok(url) => valid_urls.push(url),\n"
|
|
" Err(err) => {\n"
|
|
" println!(\"On {base_url}: could not parse {href:?}: {err} (ignored)\",);\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" Ok(valid_urls)\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let start_url = Url::parse(\"https://www.google.org\").unwrap();\n"
|
|
" let response = get(start_url).unwrap();\n"
|
|
" match extract_links(response) {\n"
|
|
" Ok(links) => println!(\"Links: {links:#?}\"),\n"
|
|
" Err(err) => println!(\"Could not extract links: {err:#}\"),\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:90
|
|
msgid "Run the code in `src/main.rs` with"
|
|
msgstr "아래 커맨드로 소스를 실행합니다"
|
|
|
|
#: src/exercises/day-4/link-checker.md:92
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ cargo run\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/link-checker.md:96
|
|
msgid "## Tasks"
|
|
msgstr "## 할 일"
|
|
|
|
#: src/exercises/day-4/link-checker.md:98
|
|
msgid ""
|
|
"* Use threads to check the links in parallel: send the URLs to be checked to a\n"
|
|
" channel and let a few threads check the URLs in parallel.\n"
|
|
"* Extend this to recursively extract links from all pages on the\n"
|
|
" `www.google.org` domain. Put an upper limit of 100 pages or so so that you\n"
|
|
" don't end up being blocked by the site."
|
|
msgstr ""
|
|
"* 스레드를 사용하여 링크를 병렬로 확인합니다: URL을 채널로 보내서 몇 개의 스레드가 URL을 병렬로 체크하도록 합니다.\n"
|
|
"* `www.google.org`도메인의 모든 페이지를 재귀적으로 확인하기 위해 코드를 확장해서 작성합니다: 차단당하지 않도록 100페이지 정도로 제한을 두시기 바랍니다."
|
|
|
|
#: src/android.md:1
|
|
msgid "# Android"
|
|
msgstr "# 안드로이드"
|
|
|
|
#: src/android.md:3
|
|
msgid ""
|
|
"Rust is supported for native platform development on Android. This means that\n"
|
|
"you can write new operating system services in Rust, as well as extending\n"
|
|
"existing services."
|
|
msgstr "러스트는 안드로이드 네이티브 플랫폼 개발을 지원합니다. 기존의 OS 서비스를 확장하거나, 새로운 서비스를 만드는데 러스트를 쓸 수 있습니다."
|
|
|
|
#: src/android.md:7
|
|
msgid ""
|
|
"> We will attempt to call Rust from one of your own projects today. So try to\n"
|
|
"> find a little corner of your code base where we can move some lines of code to\n"
|
|
"> Rust. The fewer dependencies and \"exotic\" types the better. Something that\n"
|
|
"> parses some raw bytes would be ideal."
|
|
msgstr "> 우리는 오늘 여러분의 프로젝트에서 러스트 코드를 호출해볼 것입니다. 그 프로젝트에서 러스트로 옮길만 한 작은 부분을 정하세요. 의존성이 적고 \"특이한\" 타입이 적을 수록 좋습니다. 바이트 몇 개를 파싱하는 코드라면 완벽합니다."
|
|
|
|
#: src/android/setup.md:1
|
|
msgid "# Setup"
|
|
msgstr "# 설치"
|
|
|
|
#: src/android/setup.md:3
|
|
msgid ""
|
|
"We will be using an Android Virtual Device to test our code. Make sure you have\n"
|
|
"access to one or create a new one with:"
|
|
msgstr "안드로이드 가상 디바이스(Android Virtual Device)를 사용하여 여러분의 코드를 수행할 겁니다. 새로운 가상 디바이스를 생성하려면 아래의 명령어를 사용하세요:"
|
|
|
|
#: src/android/setup.md:6
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ source build/envsetup.sh\n"
|
|
"$ lunch aosp_cf_x86_64_phone-userdebug\n"
|
|
"$ acloud create\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/setup.md:12
|
|
msgid ""
|
|
"Please see the [Android Developer\n"
|
|
"Codelab](https://source.android.com/docs/setup/start) for details."
|
|
msgstr "자세한 내용은 [Android Developer Codelab](https://source.android.com/docs/setup/start)을 참조하십시오."
|
|
|
|
#: src/android/build-rules.md:1
|
|
msgid "# Build Rules"
|
|
msgstr "# 빌드 규칙(Build Rules)"
|
|
|
|
#: src/android/build-rules.md:3
|
|
msgid "The Android build system (Soong) supports Rust via a number of modules:"
|
|
msgstr "안드로이드 빌드 시스템(Soong)은 다음과 같은 여러 모듈을 통해 러스트를 지원합니다:"
|
|
|
|
#: src/android/build-rules.md:5
|
|
msgid ""
|
|
"| Module Type | Description |\n"
|
|
"|-------------------|----------------------------------------------------------------------------------------------------|\n"
|
|
"| `rust_binary` | Produces a Rust binary. |\n"
|
|
"| `rust_library` | Produces a Rust library, and provides both `rlib` and `dylib` variants. |\n"
|
|
"| `rust_ffi` | Produces a Rust C library usable by `cc` modules, and provides both static and shared variants. |\n"
|
|
"| `rust_proc_macro` | Produces a `proc-macro` Rust library. These are analogous to compiler plugins. |\n"
|
|
"| `rust_test` | Produces a Rust test binary that uses the standard Rust test harness. |\n"
|
|
"| `rust_fuzz` | Produces a Rust fuzz binary leveraging `libfuzzer`. |\n"
|
|
"| `rust_protobuf` | Generates source and produces a Rust library that provides an interface for a particular protobuf. |\n"
|
|
"| `rust_bindgen` | Generates source and produces a Rust library containing Rust bindings to C libraries. |"
|
|
msgstr ""
|
|
"| Module Type | Description |\n"
|
|
"|-------------------|------------------------------------------------------|\n"
|
|
"| `rust_binary` | 러스트 바이너리를 생성합니다.|\n"
|
|
"| `rust_library` | 러스트 라이브러리(rlib혹은 dylib)를 생성합니다.|\n"
|
|
"| `rust_ffi` | cc 모듈에서 사용할 수 있는 C library (정적 혹은 동적)를 생성합니다.|\n"
|
|
"| `rust_proc_macro` | `proc-macro`를 구현하는 러스트 라이브러리를 생성합니다. 컴파일러의 플러그인으로 생각해도 좋습니다.|\n"
|
|
"| `rust_test` | 표준 러스트 테스트 러너를 사용하는 테스트 바이너리를 생성합니다.|\n"
|
|
"| `rust_fuzz` | `libfuzzer`를 사용하여 fuzz 바이너리를 생성합니다.|\n"
|
|
"| `rust_protobuf` | 프로토버프(protobuf) 인터페이스를 제공하는 러스트 라이브러리를 생성합니다.|\n"
|
|
"| `rust_bindgen` | C 라이브러리에 대한 러스트 바인딩을 제공하는 러스트 라이브러리를 생성합니다.|"
|
|
|
|
#: src/android/build-rules.md:16
|
|
msgid "We will look at `rust_binary` and `rust_library` next."
|
|
msgstr "다음은 `rust_binary`와 `rust_library`를 살펴봅니다."
|
|
|
|
#: src/android/build-rules/binary.md:1
|
|
msgid "# Rust Binaries"
|
|
msgstr "# 러스트 바이너리"
|
|
|
|
#: src/android/build-rules/binary.md:3
|
|
msgid ""
|
|
"Let us start with a simple application. At the root of an AOSP checkout, create\n"
|
|
"the following files:"
|
|
msgstr "간단한 응용 프로그램으로 시작해 보겠습니다. AOSP 체크아웃의 루트에서 다음 파일을 생성합니다:"
|
|
|
|
#: src/android/build-rules/binary.md:6 src/android/build-rules/library.md:13
|
|
msgid "_hello_rust/Android.bp_:"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/binary.md:8
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"hello_rust\",\n"
|
|
" crate_name: \"hello_rust\",\n"
|
|
" srcs: [\"src/main.rs\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/binary.md:16 src/android/build-rules/library.md:34
|
|
msgid "_hello_rust/src/main.rs_:"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/binary.md:18
|
|
msgid ""
|
|
"```rust\n"
|
|
"//! Rust demo.\n"
|
|
"\n"
|
|
"/// Prints a greeting to standard output.\n"
|
|
"fn main() {\n"
|
|
" println!(\"Hello from Rust!\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/binary.md:27
|
|
msgid "You can now build, push, and run the binary:"
|
|
msgstr "그런 다음, 이 바이너리를 빌드하고, 가상 디바이스에 넣고, 실행합니다:"
|
|
|
|
#: src/android/build-rules/binary.md:29
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m hello_rust\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/hello_rust\n"
|
|
"Hello from Rust!\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/library.md:1
|
|
msgid "# Rust Libraries"
|
|
msgstr "# 러스트 라이브러리"
|
|
|
|
#: src/android/build-rules/library.md:3
|
|
msgid "You use `rust_library` to create a new Rust library for Android."
|
|
msgstr "`rust_library`를 사용하여 안드로이드용 새 러스트 라이브러리를 만듭니다."
|
|
|
|
#: src/android/build-rules/library.md:5
|
|
msgid "Here we declare a dependency on two libraries:"
|
|
msgstr "여기서 두 개의 라이브러리에 대한 의존성을 선언합니다:"
|
|
|
|
#: src/android/build-rules/library.md:7
|
|
msgid ""
|
|
"* `libgreeting`, which we define below,\n"
|
|
"* `libtextwrap`, which is a crate already vendored in\n"
|
|
" [`external/rust/crates/`][crates]."
|
|
msgstr ""
|
|
"* 아래에 정의한 `libgreeting`.\n"
|
|
"* [`external/rust/crates/`][crates]에 존재하는 `libtextwrap`."
|
|
|
|
#: src/android/build-rules/library.md:15
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"hello_rust_with_dep\",\n"
|
|
" crate_name: \"hello_rust_with_dep\",\n"
|
|
" srcs: [\"src/main.rs\"],\n"
|
|
" rustlibs: [\n"
|
|
" \"libgreetings\",\n"
|
|
" \"libtextwrap\",\n"
|
|
" ],\n"
|
|
" prefer_rlib: true,\n"
|
|
"}\n"
|
|
"\n"
|
|
"rust_library {\n"
|
|
" name: \"libgreetings\",\n"
|
|
" crate_name: \"greetings\",\n"
|
|
" srcs: [\"src/lib.rs\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/library.md:36
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Rust demo.\n"
|
|
"\n"
|
|
"use greetings::greeting;\n"
|
|
"use textwrap::fill;\n"
|
|
"\n"
|
|
"/// Prints a greeting to standard output.\n"
|
|
"fn main() {\n"
|
|
" println!(\"{}\", fill(&greeting(\"Bob\"), 24));\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/library.md:48
|
|
msgid "_hello_rust/src/lib.rs_:"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/library.md:50
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Greeting library.\n"
|
|
"\n"
|
|
"/// Greet `name`.\n"
|
|
"pub fn greeting(name: &str) -> String {\n"
|
|
" format!(\"Hello {name}, it is very nice to meet you!\")\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/build-rules/library.md:59
|
|
msgid "You build, push, and run the binary like before:"
|
|
msgstr "이전처럼, 빌드하고, 가상 디바이스로 넣고, 실행합니다:"
|
|
|
|
#: src/android/build-rules/library.md:61
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m hello_rust_with_dep\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/hello_rust_with_dep\n"
|
|
"Hello Bob, it is very\n"
|
|
"nice to meet you!\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl.md:1
|
|
msgid "# AIDL"
|
|
msgstr "# AIDL"
|
|
|
|
#: src/android/aidl.md:3
|
|
msgid ""
|
|
"The [Android Interface Definition Language\n"
|
|
"(AIDL)](https://developer.android.com/guide/components/aidl) is supported in Rust:"
|
|
msgstr "러스트는 [안드로이드 인터페이스 정의 언어(AIDL)](https://developer.android.com/guide/components/aidl)를 지원합니다:"
|
|
|
|
#: src/android/aidl.md:6
|
|
msgid ""
|
|
"* Rust code can call existing AIDL servers,\n"
|
|
"* You can create new AIDL servers in Rust."
|
|
msgstr ""
|
|
"* 러스트 코드에서 기존 AIDL 서버를 호출 할 수 있습니다. \n"
|
|
"* 러스트에서 새로운 AIDL 서버를 생성할 수 있습니다."
|
|
|
|
#: src/android/aidl/interface.md:1
|
|
msgid "# AIDL Interfaces"
|
|
msgstr "# AIDL 인터페이스"
|
|
|
|
#: src/android/aidl/interface.md:3
|
|
msgid "You declare the API of your service using an AIDL interface:"
|
|
msgstr "AIDL 인터페이스를 이용해서 서비스의 API를 선언합니다:"
|
|
|
|
#: src/android/aidl/interface.md:5
|
|
msgid "*birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/interface.md:7
|
|
msgid ""
|
|
"```java\n"
|
|
"package com.example.birthdayservice;\n"
|
|
"\n"
|
|
"/** Birthday service interface. */\n"
|
|
"interface IBirthdayService {\n"
|
|
" /** Generate a Happy Birthday message. */\n"
|
|
" String wishHappyBirthday(String name, int years);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/interface.md:17
|
|
msgid "*birthday_service/aidl/Android.bp*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/interface.md:19
|
|
msgid ""
|
|
"```javascript\n"
|
|
"aidl_interface {\n"
|
|
" name: \"com.example.birthdayservice\",\n"
|
|
" srcs: [\"com/example/birthdayservice/*.aidl\"],\n"
|
|
" unstable: true,\n"
|
|
" backend: {\n"
|
|
" rust: { // Rust is not enabled by default\n"
|
|
" enabled: true,\n"
|
|
" },\n"
|
|
" },\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/interface.md:32
|
|
msgid ""
|
|
"Add `vendor_available: true` if your AIDL file is used by a binary in the vendor\n"
|
|
"partition."
|
|
msgstr "AIDL 파일이 벤더 파티션에 있는 바이너리에서 사용될 경우 `vendor_available: true`를 추가합니다."
|
|
|
|
#: src/android/aidl/implementation.md:1
|
|
msgid "# Service Implementation"
|
|
msgstr "# 서비스 구현"
|
|
|
|
#: src/android/aidl/implementation.md:3
|
|
msgid "We can now implement the AIDL service:"
|
|
msgstr "이제 AIDL서비스를 구현할 수 있습니다:"
|
|
|
|
#: src/android/aidl/implementation.md:5
|
|
msgid "*birthday_service/src/lib.rs*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/implementation.md:7
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Implementation of the `IBirthdayService` AIDL interface.\n"
|
|
"use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;\n"
|
|
"use com_example_birthdayservice::binder;\n"
|
|
"\n"
|
|
"/// The `IBirthdayService` implementation.\n"
|
|
"pub struct BirthdayService;\n"
|
|
"\n"
|
|
"impl binder::Interface for BirthdayService {}\n"
|
|
"\n"
|
|
"impl IBirthdayService for BirthdayService {\n"
|
|
" fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String> {\n"
|
|
" Ok(format!(\n"
|
|
" \"Happy Birthday {name}, congratulations with the {years} years!\"\n"
|
|
" ))\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/implementation.md:26 src/android/aidl/server.md:28
|
|
#: src/android/aidl/client.md:37
|
|
msgid "*birthday_service/Android.bp*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/implementation.md:28
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_library {\n"
|
|
" name: \"libbirthdayservice\",\n"
|
|
" srcs: [\"src/lib.rs\"],\n"
|
|
" crate_name: \"birthdayservice\",\n"
|
|
" rustlibs: [\n"
|
|
" \"com.example.birthdayservice-rust\",\n"
|
|
" \"libbinder_rs\",\n"
|
|
" ],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/server.md:1
|
|
msgid "# AIDL Server"
|
|
msgstr "# AIDL 서버"
|
|
|
|
#: src/android/aidl/server.md:3
|
|
msgid "Finally, we can create a server which exposes the service:"
|
|
msgstr "마지막으로 서비스를 제공하는 서버를 만들 수 있습니다:"
|
|
|
|
#: src/android/aidl/server.md:5
|
|
msgid "*birthday_service/src/server.rs*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/server.md:7
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Birthday service.\n"
|
|
"use birthdayservice::BirthdayService;\n"
|
|
"use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::BnBirthdayService;\n"
|
|
"use com_example_birthdayservice::binder;\n"
|
|
"\n"
|
|
"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n"
|
|
"\n"
|
|
"/// Entry point for birthday service.\n"
|
|
"fn main() {\n"
|
|
" let birthday_service = BirthdayService;\n"
|
|
" let birthday_service_binder = BnBirthdayService::new_binder(\n"
|
|
" birthday_service,\n"
|
|
" binder::BinderFeatures::default(),\n"
|
|
" );\n"
|
|
" binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder())\n"
|
|
" .expect(\"Failed to register service\");\n"
|
|
" binder::ProcessState::join_thread_pool()\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/server.md:30
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"birthday_server\",\n"
|
|
" crate_name: \"birthday_server\",\n"
|
|
" srcs: [\"src/server.rs\"],\n"
|
|
" rustlibs: [\n"
|
|
" \"com.example.birthdayservice-rust\",\n"
|
|
" \"libbinder_rs\",\n"
|
|
" \"libbirthdayservice\",\n"
|
|
" ],\n"
|
|
" prefer_rlib: true,\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/deploy.md:1
|
|
msgid "# Deploy"
|
|
msgstr "# 배포"
|
|
|
|
#: src/android/aidl/deploy.md:3
|
|
msgid "We can now build, push, and start the service:"
|
|
msgstr "서비스를 빌드하고, 가상 디바이스에 넣고, 시작 할 수 있습니다:"
|
|
|
|
#: src/android/aidl/deploy.md:5
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m birthday_server\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/birthday_server\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/deploy.md:11
|
|
msgid "In another terminal, check that the service runs:"
|
|
msgstr "다른 터미널을 띄워서 서비스가 잘 수행되고 있는지 확인합니다:"
|
|
|
|
#: src/android/aidl/deploy.md:13
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ adb shell service check birthdayservice\n"
|
|
"Service birthdayservice: found\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/deploy.md:18
|
|
msgid "You can also call the service with `service call`:"
|
|
msgstr "`service call`명렁어로 서비스를 호출할 수도 있습니다:"
|
|
|
|
#: src/android/aidl/deploy.md:20
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n"
|
|
"Result: Parcel(\n"
|
|
" 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'\n"
|
|
" 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'\n"
|
|
" 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n"
|
|
" 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n"
|
|
" 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n"
|
|
" 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n"
|
|
" 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n"
|
|
" 0x00000070: 00210073 00000000 's.!..... ')\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/client.md:1
|
|
msgid "# AIDL Client"
|
|
msgstr "# AIDL 클라이언트"
|
|
|
|
#: src/android/aidl/client.md:3
|
|
msgid "Finally, we can create a Rust client for our new service."
|
|
msgstr "마지막으로, 아까 추가한 서비스에 대한 클라이언트를 러스트로 만들겠습니다."
|
|
|
|
#: src/android/aidl/client.md:5
|
|
msgid "*birthday_service/src/client.rs*:"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/client.md:7
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Birthday service.\n"
|
|
"use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;\n"
|
|
"use com_example_birthdayservice::binder;\n"
|
|
"\n"
|
|
"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n"
|
|
"\n"
|
|
"/// Connect to the BirthdayService.\n"
|
|
"pub fn connect() -> Result<binder::Strong<dyn IBirthdayService>, binder::StatusCode> {\n"
|
|
" binder::get_interface(SERVICE_IDENTIFIER)\n"
|
|
"}\n"
|
|
"\n"
|
|
"/// Call the birthday service.\n"
|
|
"fn main() -> Result<(), binder::Status> {\n"
|
|
" let name = std::env::args()\n"
|
|
" .nth(1)\n"
|
|
" .unwrap_or_else(|| String::from(\"Bob\"));\n"
|
|
" let years = std::env::args()\n"
|
|
" .nth(2)\n"
|
|
" .and_then(|arg| arg.parse::<i32>().ok())\n"
|
|
" .unwrap_or(42);\n"
|
|
"\n"
|
|
" binder::ProcessState::start_thread_pool();\n"
|
|
" let service = connect().expect(\"Failed to connect to BirthdayService\");\n"
|
|
" let msg = service.wishHappyBirthday(&name, years)?;\n"
|
|
" println!(\"{msg}\");\n"
|
|
" Ok(())\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/client.md:39
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"birthday_client\",\n"
|
|
" crate_name: \"birthday_client\",\n"
|
|
" srcs: [\"src/client.rs\"],\n"
|
|
" rustlibs: [\n"
|
|
" \"com.example.birthdayservice-rust\",\n"
|
|
" \"libbinder_rs\",\n"
|
|
" ],\n"
|
|
" prefer_rlib: true,\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/client.md:52
|
|
msgid "Notice that the client does not depend on `libbirthdayservice`."
|
|
msgstr "클라이언트는 `libbirthdayservice`에 의존하지 않음에 주목하세요."
|
|
|
|
#: src/android/aidl/client.md:54
|
|
msgid "Build, push, and run the client on your device:"
|
|
msgstr "빌드하고, 가상 디바이스로 넣고, 실행합니다:"
|
|
|
|
#: src/android/aidl/client.md:56
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m birthday_client\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/birthday_client Charlie 60\n"
|
|
"Happy Birthday Charlie, congratulations with the 60 years!\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/aidl/changing.md:1
|
|
msgid "# Changing API"
|
|
msgstr "# API 수정"
|
|
|
|
#: src/android/aidl/changing.md:3
|
|
msgid ""
|
|
"Let us extend the API with more functionality: we want to let clients specify a\n"
|
|
"list of lines for the birthday card:"
|
|
msgstr "API를 확장하여 더 많은 기능을 제공해 봅시다. 클라이언트가 생일 카드에 담길 내용을 지정할 수 있도록 하겠습니다:"
|
|
|
|
#: src/android/aidl/changing.md:6
|
|
msgid ""
|
|
"```java\n"
|
|
"package com.example.birthdayservice;\n"
|
|
"\n"
|
|
"/** Birthday service interface. */\n"
|
|
"interface IBirthdayService {\n"
|
|
" /** Generate a Happy Birthday message. */\n"
|
|
" String wishHappyBirthday(String name, int years, in String[] text);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:1
|
|
msgid "# Logging"
|
|
msgstr "# 로깅"
|
|
|
|
#: src/android/logging.md:3
|
|
msgid ""
|
|
"You should use the `log` crate to automatically log to `logcat` (on-device) or\n"
|
|
"`stdout` (on-host):"
|
|
msgstr "`log` 크레이트를 사용하면 안드로이드 디바이스 안에서 수행될 때에는 `logcat`으로, 호스트에서 수행될 때에는 `stdout`으로 로그가 자동으로 출력이 되도록 할 수 있습니다:"
|
|
|
|
#: src/android/logging.md:6
|
|
msgid "_hello_rust_logs/Android.bp_:"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:8
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"hello_rust_logs\",\n"
|
|
" crate_name: \"hello_rust_logs\",\n"
|
|
" srcs: [\"src/main.rs\"],\n"
|
|
" rustlibs: [\n"
|
|
" \"liblog_rust\",\n"
|
|
" \"liblogger\",\n"
|
|
" ],\n"
|
|
" prefer_rlib: true,\n"
|
|
" host_supported: true,\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:22
|
|
msgid "_hello_rust_logs/src/main.rs_:"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:24
|
|
msgid ""
|
|
"```rust,ignore\n"
|
|
"//! Rust logging demo.\n"
|
|
"\n"
|
|
"use log::{debug, error, info};\n"
|
|
"\n"
|
|
"/// Logs a greeting.\n"
|
|
"fn main() {\n"
|
|
" logger::init(\n"
|
|
" logger::Config::default()\n"
|
|
" .with_tag_on_device(\"rust\")\n"
|
|
" .with_min_level(log::Level::Trace),\n"
|
|
" );\n"
|
|
" debug!(\"Starting program.\");\n"
|
|
" info!(\"Things are going fine.\");\n"
|
|
" error!(\"Something went wrong!\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:42 src/android/interoperability/with-c/bindgen.md:98
|
|
#: src/android/interoperability/with-c/rust.md:73
|
|
msgid "Build, push, and run the binary on your device:"
|
|
msgstr "빌드하고, 가상 디바이스에 넣고, 실행합니다:"
|
|
|
|
#: src/android/logging.md:44
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m hello_rust_logs\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/hello_rust_logs\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/logging.md:50
|
|
msgid "The logs show up in `adb logcat`:"
|
|
msgstr "`adb logcat`커맨드로 로그를 확인합니다:"
|
|
|
|
#: src/android/logging.md:52
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ adb logcat -s rust\n"
|
|
"09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.\n"
|
|
"09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine.\n"
|
|
"09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong!\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability.md:1
|
|
msgid "# Interoperability"
|
|
msgstr "# 상호운용성(Interoperability)"
|
|
|
|
#: src/android/interoperability.md:3
|
|
msgid ""
|
|
"Rust has excellent support for interoperability with other languages. This means\n"
|
|
"that you can:"
|
|
msgstr "러스트는 다음과 같이 다른 언어와의 상호운용성을 훌륭히 지원합니다:"
|
|
|
|
#: src/android/interoperability.md:6
|
|
msgid ""
|
|
"* Call Rust functions from other languages.\n"
|
|
"* Call functions written in other languages from Rust."
|
|
msgstr ""
|
|
"* 타 언어에서 러스트 함수를 호출합니다.\n"
|
|
"* 타 언어의 함수를 러스트에서 호출합니다."
|
|
|
|
#: src/android/interoperability.md:9
|
|
msgid ""
|
|
"When you call functions in a foreign language we say that you're using a\n"
|
|
"_foreign function interface_, also known as FFI."
|
|
msgstr "타 언어의 함수를 호출해서 사용하는 것을 FFI(foreign function interface)라고 합니다."
|
|
|
|
#: src/android/interoperability/with-c.md:1
|
|
msgid "# Interoperability with C"
|
|
msgstr "# C와의 상호운용성"
|
|
|
|
#: src/android/interoperability/with-c.md:3
|
|
msgid ""
|
|
"Rust has full support for linking object files with a C calling convention.\n"
|
|
"Similarly, you can export Rust functions and call them from C."
|
|
msgstr "러스트는 C 호출규약을 따르는 오브젝트 파일과 링킹할 수 있습니다. 또한, 반대로 러스트 함수를 내보내서 C에서 호출 할 수 도 있습니다."
|
|
|
|
#: src/android/interoperability/with-c.md:6
|
|
msgid "You can do it by hand if you want:"
|
|
msgstr "원한다면 아래와 같이 수동으로 코딩할 수 있습니다:"
|
|
|
|
#: src/android/interoperability/with-c.md:8
|
|
msgid ""
|
|
"```rust\n"
|
|
"extern \"C\" {\n"
|
|
" fn abs(x: i32) -> i32;\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let x = -42;\n"
|
|
" let abs_x = unsafe { abs(x) };\n"
|
|
" println!(\"{x}, {abs_x}\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c.md:20
|
|
msgid ""
|
|
"We already saw this in the [Safe FFI Wrapper\n"
|
|
"exercise](../../exercises/day-3/safe-ffi-wrapper.md)."
|
|
msgstr "우리는 이미 [Safe FFI 래퍼 연습문제](../../exercises/day-3/safe-ffi-wrapper.md)에서 이를 다루었습니다."
|
|
|
|
#: src/android/interoperability/with-c.md:23
|
|
msgid ""
|
|
"> This assumes full knowledge of the target platform. Not recommended for\n"
|
|
"> production."
|
|
msgstr "> 이러한 방법은 타겟 플랫폼의 모든 부분을 사전에 알고 있다는 전제를 깔고 있습니다. 상용 프로젝트에서는 권장하지 않습니다."
|
|
|
|
#: src/android/interoperability/with-c.md:26
|
|
msgid "We will look at better options next."
|
|
msgstr "좀 더 나은 옵션을 살펴보겠습니다."
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:1
|
|
msgid "# Using Bindgen"
|
|
msgstr "# Bindgen 사용하기"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:3
|
|
msgid ""
|
|
"The [bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html) tool\n"
|
|
"can auto-generate bindings from a C header file."
|
|
msgstr "[bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html)는 C 헤더파일에서 러스트 바인딩을 자동으로 생성하는 도구입니다."
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:6
|
|
msgid "First create a small C library:"
|
|
msgstr "먼저 작은 C라이브러리를 만들어 보겠습니다:"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:8
|
|
msgid "_interoperability/bindgen/libbirthday.h_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:10
|
|
msgid ""
|
|
"```c\n"
|
|
"typedef struct card {\n"
|
|
" const char* name;\n"
|
|
" int years;\n"
|
|
"} card;\n"
|
|
"\n"
|
|
"void print_card(const card* card);\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:19
|
|
msgid "_interoperability/bindgen/libbirthday.c_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:21
|
|
msgid ""
|
|
"```c\n"
|
|
"#include <stdio.h>\n"
|
|
"#include \"libbirthday.h\"\n"
|
|
"\n"
|
|
"void print_card(const card* card) {\n"
|
|
" printf(\"+--------------\\n\");\n"
|
|
" printf(\"| Happy Birthday %s!\\n\", card->name);\n"
|
|
" printf(\"| Congratulations with the %i years!\\n\", card->years);\n"
|
|
" printf(\"+--------------\\n\");\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:33
|
|
msgid "Add this to your `Android.bp` file:"
|
|
msgstr "`Android.bp` 파일에 아래를 추가합니다:"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:35
|
|
#: src/android/interoperability/with-c/bindgen.md:55
|
|
#: src/android/interoperability/with-c/bindgen.md:69
|
|
#: src/android/interoperability/with-c/bindgen.md:108
|
|
msgid "_interoperability/bindgen/Android.bp_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:37
|
|
msgid ""
|
|
"```javascript\n"
|
|
"cc_library {\n"
|
|
" name: \"libbirthday\",\n"
|
|
" srcs: [\"libbirthday.c\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:44
|
|
msgid ""
|
|
"Create a wrapper header file for the library (not strictly needed in this\n"
|
|
"example):"
|
|
msgstr "라이브러리에 대한 헤더 파일을 만듭니다(이 예시에서는 반드시 필요한 것은 아닙니다.):"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:47
|
|
msgid "_interoperability/bindgen/libbirthday_wrapper.h_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:49
|
|
msgid ""
|
|
"```c\n"
|
|
"#include \"libbirthday.h\"\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:53
|
|
msgid "You can now auto-generate the bindings:"
|
|
msgstr "이제 바인딩을 자동으로 생성할 수 있습니다:"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:57
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_bindgen {\n"
|
|
" name: \"libbirthday_bindgen\",\n"
|
|
" crate_name: \"birthday_bindgen\",\n"
|
|
" wrapper_src: \"libbirthday_wrapper.h\",\n"
|
|
" source_stem: \"bindings\",\n"
|
|
" static_libs: [\"libbirthday\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:67
|
|
msgid "Finally, we can use the bindings in our Rust program:"
|
|
msgstr "마침내, 러스트 프로그램에서 바인딩을 사용할 수 있습니다:"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:71
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_binary {\n"
|
|
" name: \"print_birthday_card\",\n"
|
|
" srcs: [\"main.rs\"],\n"
|
|
" rustlibs: [\"libbirthday_bindgen\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:79
|
|
msgid "_interoperability/bindgen/main.rs_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:81
|
|
msgid ""
|
|
"```rust,compile_fail\n"
|
|
"//! Bindgen demo.\n"
|
|
"\n"
|
|
"use birthday_bindgen::{card, print_card};\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let name = std::ffi::CString::new(\"Peter\").unwrap();\n"
|
|
" let card = card {\n"
|
|
" name: name.as_ptr(),\n"
|
|
" years: 42,\n"
|
|
" };\n"
|
|
" unsafe {\n"
|
|
" print_card(&card as *const card);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:100
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m print_birthday_card\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/print_birthday_card\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:106
|
|
msgid "Finally, we can run auto-generated tests to ensure the bindings work:"
|
|
msgstr "마지막으로, 바인딩이 잘 작동하는지 확인하기 위해, 자동 생성된 테스트를 실행해 보겠습니다:"
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:110
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_test {\n"
|
|
" name: \"libbirthday_bindgen_test\",\n"
|
|
" srcs: [\":libbirthday_bindgen\"],\n"
|
|
" crate_name: \"libbirthday_bindgen_test\",\n"
|
|
" test_suites: [\"general-tests\"],\n"
|
|
" auto_gen_config: true,\n"
|
|
" clippy_lints: \"none\", // Generated file, skip linting\n"
|
|
" lints: \"none\",\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/bindgen.md:122
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ atest libbirthday_bindgen_test\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:1
|
|
msgid "# Calling Rust"
|
|
msgstr "# C에서 러스트 호출"
|
|
|
|
#: src/android/interoperability/with-c/rust.md:3
|
|
msgid "Exporting Rust functions and types to C is easy:"
|
|
msgstr "러스트에서 타입과 함수를 C로 내보내는 것은 간단합니다:"
|
|
|
|
#: src/android/interoperability/with-c/rust.md:5
|
|
msgid "_interoperability/rust/libanalyze/analyze.rs_"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:7
|
|
msgid ""
|
|
"```rust,editable\n"
|
|
"//! Rust FFI demo.\n"
|
|
"#![deny(improper_ctypes_definitions)]\n"
|
|
"\n"
|
|
"use std::os::raw::c_int;\n"
|
|
"\n"
|
|
"/// Analyze the numbers.\n"
|
|
"#[no_mangle]\n"
|
|
"pub extern \"C\" fn analyze_numbers(x: c_int, y: c_int) {\n"
|
|
" if x < y {\n"
|
|
" println!(\"x ({x}) is smallest!\");\n"
|
|
" } else {\n"
|
|
" println!(\"y ({y}) is probably larger than x ({x})\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:24
|
|
msgid "_interoperability/rust/libanalyze/analyze.h_"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:26
|
|
msgid ""
|
|
"```c\n"
|
|
"#ifndef ANALYSE_H\n"
|
|
"#define ANALYSE_H\n"
|
|
"\n"
|
|
"extern \"C\" {\n"
|
|
"void analyze_numbers(int x, int y);\n"
|
|
"}\n"
|
|
"\n"
|
|
"#endif\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:37
|
|
msgid "_interoperability/rust/libanalyze/Android.bp_"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:39
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_ffi {\n"
|
|
" name: \"libanalyze_ffi\",\n"
|
|
" crate_name: \"analyze_ffi\",\n"
|
|
" srcs: [\"analyze.rs\"],\n"
|
|
" include_dirs: [\".\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:48
|
|
msgid "We can now call this from a C binary:"
|
|
msgstr "이제 이 러스트 함수를 C바이너리에서 호출할 수 있습니다:"
|
|
|
|
#: src/android/interoperability/with-c/rust.md:50
|
|
msgid "_interoperability/rust/analyze/main.c_"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:52
|
|
msgid ""
|
|
"```c\n"
|
|
"#include \"analyze.h\"\n"
|
|
"\n"
|
|
"int main() {\n"
|
|
" analyze_numbers(10, 20);\n"
|
|
" analyze_numbers(123, 123);\n"
|
|
" return 0;\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:62
|
|
msgid "_interoperability/rust/analyze/Android.bp_"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:64
|
|
msgid ""
|
|
"```javascript\n"
|
|
"cc_binary {\n"
|
|
" name: \"analyze_numbers\",\n"
|
|
" srcs: [\"main.c\"],\n"
|
|
" static_libs: [\"libanalyze_ffi\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:75
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m analyze_numbers\n"
|
|
"$ adb push $ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/tmp\n"
|
|
"$ adb shell /data/local/tmp/analyze_numbers\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/with-c/rust.md:83
|
|
msgid ""
|
|
"`#[no_mangle]` disables Rust's usual name mangling, so the exported symbol will just be the name of\n"
|
|
"the function. You can also use `#[export_name = \"some_name\"]` to specify whatever name you want."
|
|
msgstr "`#[no_mangle]`은 러스트의 네임 맹글링(name mangling)을 비활성화하므로 외부로 노출되는 심볼의 이름은 함수의 이름 그대로가 됩니다. 심볼 이름을 바꾸고 싶다면 `#[export_name = \"some_name\"]`을 사용합니다."
|
|
|
|
#: src/android/interoperability/cpp.md:1
|
|
msgid "# With C++"
|
|
msgstr "# C++ 와의 상호운용성"
|
|
|
|
#: src/android/interoperability/cpp.md:3
|
|
msgid ""
|
|
"The [CXX crate][1] makes it possible to do safe interoperability between Rust\n"
|
|
"and C++."
|
|
msgstr "[CXX 크레이트][1]는 러스트와 C++ 사이의 안전한 상호운용성을 가능하게 해줍니다."
|
|
|
|
#: src/android/interoperability/cpp.md:6
|
|
msgid "The overall approach looks like this:"
|
|
msgstr "전체적인 접근 방법은 다음과 같습니다:"
|
|
|
|
#: src/android/interoperability/cpp.md:8
|
|
msgid "<img src=\"cpp/overview.svg\">"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/cpp.md:10
|
|
msgid "See the [CXX tutorial][2] for an full example of using this."
|
|
msgstr "사용하는 방법에 대해서는[CXX 튜토리얼][2] 를 참조합니다."
|
|
|
|
#: src/android/interoperability/java.md:1
|
|
msgid "# Interoperability with Java"
|
|
msgstr "# Java와의 상호운용성"
|
|
|
|
#: src/android/interoperability/java.md:3
|
|
msgid ""
|
|
"Java can load shared objects via [Java Native Interface\n"
|
|
"(JNI)](https://en.wikipedia.org/wiki/Java_Native_Interface). The [`jni`\n"
|
|
"crate](https://docs.rs/jni/) allows you to create a compatible library."
|
|
msgstr "자바는 [Java Native Interface(JNI)](https://en.wikipedia.org/wiki/Java_Native_Interface)를 통해 공유 라이브러리를 로드할 수 있습니다. [`jni` 크레이트](https://docs.rs/jni/)를 사용하여 JNI 라이브러리를 만들 수 있습니다."
|
|
|
|
#: src/android/interoperability/java.md:7
|
|
msgid "First, we create a Rust function to export to Java:"
|
|
msgstr "먼저, 자바로 내보낼 러스트 함수를 생성합니다:"
|
|
|
|
#: src/android/interoperability/java.md:9
|
|
msgid "_interoperability/java/src/lib.rs_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:11
|
|
msgid ""
|
|
"```rust,compile_fail\n"
|
|
"//! Rust <-> Java FFI demo.\n"
|
|
"\n"
|
|
"use jni::objects::{JClass, JString};\n"
|
|
"use jni::sys::jstring;\n"
|
|
"use jni::JNIEnv;\n"
|
|
"\n"
|
|
"/// HelloWorld::hello method implementation.\n"
|
|
"#[no_mangle]\n"
|
|
"pub extern \"system\" fn Java_HelloWorld_hello(\n"
|
|
" env: JNIEnv,\n"
|
|
" _class: JClass,\n"
|
|
" name: JString,\n"
|
|
") -> jstring {\n"
|
|
" let input: String = env.get_string(name).unwrap().into();\n"
|
|
" let greeting = format!(\"Hello, {input}!\");\n"
|
|
" let output = env.new_string(greeting).unwrap();\n"
|
|
" output.into_inner()\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:32
|
|
#: src/android/interoperability/java.md:62
|
|
msgid "_interoperability/java/Android.bp_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:34
|
|
msgid ""
|
|
"```javascript\n"
|
|
"rust_ffi_shared {\n"
|
|
" name: \"libhello_jni\",\n"
|
|
" crate_name: \"hello_jni\",\n"
|
|
" srcs: [\"src/lib.rs\"],\n"
|
|
" rustlibs: [\"libjni\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:43
|
|
msgid "Finally, we can call this function from Java:"
|
|
msgstr "자바에서 이 함수를 호출 할 수 있습니다:"
|
|
|
|
#: src/android/interoperability/java.md:45
|
|
msgid "_interoperability/java/HelloWorld.java_:"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:47
|
|
msgid ""
|
|
"```java\n"
|
|
"class HelloWorld {\n"
|
|
" private static native String hello(String name);\n"
|
|
"\n"
|
|
" static {\n"
|
|
" System.loadLibrary(\"hello_jni\");\n"
|
|
" }\n"
|
|
"\n"
|
|
" public static void main(String[] args) {\n"
|
|
" String output = HelloWorld.hello(\"Alice\");\n"
|
|
" System.out.println(output);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:64
|
|
msgid ""
|
|
"```javascript\n"
|
|
"java_binary {\n"
|
|
" name: \"helloworld_jni\",\n"
|
|
" srcs: [\"HelloWorld.java\"],\n"
|
|
" main_class: \"HelloWorld\",\n"
|
|
" required: [\"libhello_jni\"],\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/android/interoperability/java.md:73
|
|
msgid "Finally, you can build, sync, and run the binary:"
|
|
msgstr "마지막으로 바이너리를 빌드, 싱크, 실행합니다:"
|
|
|
|
#: src/android/interoperability/java.md:75
|
|
msgid ""
|
|
"```shell\n"
|
|
"$ m helloworld_jni\n"
|
|
"$ adb sync # requires adb root && adb remount\n"
|
|
"$ adb shell /system/bin/helloworld_jni\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/android.md:3
|
|
msgid ""
|
|
"For the last exercise, we will look at one of the projects you work with. Let us\n"
|
|
"group up and do this together. Some suggestions:"
|
|
msgstr "마지막 연습문제는 여러분이 작업하고 있는 프로젝트 중 하나를 FFI로 러스트와 연계 해보는 것입니다. 몇 가지 예시입니다:"
|
|
|
|
#: src/exercises/day-4/android.md:6
|
|
msgid ""
|
|
"* Call your AIDL service with a client written in Rust.\n"
|
|
"\n"
|
|
"* Move a function from your project to Rust and call it."
|
|
msgstr ""
|
|
"* 당신의 AIDL서비스를 러스트 클라이언트에서 호출해봅니다.\n"
|
|
"\n"
|
|
"* 당신의 프로젝트의 함수를 러스트로 옮기고 호출해봅니다."
|
|
|
|
#: src/exercises/day-4/android.md:12
|
|
msgid ""
|
|
"No solution is provided here since this is open-ended: it relies on someone in\n"
|
|
"the class having a piece of code which you can turn in to Rust on the fly."
|
|
msgstr "이 연습문제는 열려있기 때문에 해답이 제공되지 않습니다. 클래스에서 제출된 코드에 의존합니다."
|
|
|
|
#: src/thanks.md:1
|
|
msgid "# Thanks!"
|
|
msgstr "# 감사인사"
|
|
|
|
#: src/thanks.md:3
|
|
msgid ""
|
|
"_Thank you for taking Comprehensive Rust 🦀!_ We hope you enjoyed it and that it\n"
|
|
"was useful."
|
|
msgstr "Comprehensive Rust 🦀를 이용해 주셔서 감사합니다. 즐겁고 유익한 시간이었기를 바랍니다."
|
|
|
|
#: src/thanks.md:6
|
|
msgid ""
|
|
"We've had a lot of fun putting the course together. The course is not perfect,\n"
|
|
"so if you spotted any mistakes or have ideas for improvements, please get in\n"
|
|
"[contact with us on\n"
|
|
"GitHub](https://github.com/google/comprehensive-rust/discussions). We would love\n"
|
|
"to hear from you."
|
|
msgstr "강의가 완벽하진 않으니 실수나 개선점이 있다면 언제든지 [깃허브](https://github.com/google/comprehensive-rust/discussions)로 연락주세요."
|
|
|
|
#: src/other-resources.md:1
|
|
msgid "# Other Rust Resources"
|
|
msgstr "# 러스트 참고 자료"
|
|
|
|
#: src/other-resources.md:3
|
|
msgid ""
|
|
"The Rust community has created a wealth of high-quality and free resources\n"
|
|
"online."
|
|
msgstr "러스트 커뮤니티는 온라인에서 고품질의 무료 소스를 만들었습니다."
|
|
|
|
#: src/other-resources.md:6
|
|
msgid "## Official Documentation"
|
|
msgstr "## 공식 문서들"
|
|
|
|
#: src/other-resources.md:8
|
|
msgid "The Rust project hosts many resources. These cover Rust in general:"
|
|
msgstr "러스트 프로젝트에는 참조할 만한 자료가 많습니다. 일반적인 내용을 다루는 몇가지 참고 문서들입니다:"
|
|
|
|
#: src/other-resources.md:10
|
|
msgid ""
|
|
"* [The Rust Programming Language](https://doc.rust-lang.org/book/): the\n"
|
|
" canonical free book about Rust. Covers the language in detail and includes a\n"
|
|
" few projects for people to build.\n"
|
|
"* [Rust By Example](https://doc.rust-lang.org/rust-by-example/): covers the Rust\n"
|
|
" syntax via a series of examples which showcase different constructs. Sometimes\n"
|
|
" includes small exercises where you are asked to expand on the code in the\n"
|
|
" examples.\n"
|
|
"* [Rust Standard Library](https://doc.rust-lang.org/std/): full documentation of\n"
|
|
" the standard library for Rust.\n"
|
|
"* [The Rust Reference](https://doc.rust-lang.org/reference/): an incomplete book\n"
|
|
" which describes the Rust grammar and memory model."
|
|
msgstr ""
|
|
"* [The Rust Programming Language](https://doc.rust-lang.org/book/): 러스트에 대한 무료 표준 서적입니다. 언어에 대한 자세한 설명과 사람들이 빌드 할수 있는 몇가지 프로젝트를 포함합니다.\n"
|
|
"* [Rust By Example](https://doc.rust-lang.org/rust-by-example/): 여러 예제를 통해 러스트의 문법을 보여주며 때때로 코드를 확장하는 약간의 연습문제들이 포함되어 있습니다.\n"
|
|
"* [Rust Standard Library](https://doc.rust-lang.org/std/): 러스트 표준 라이브러리 전체 문서입니다.\n"
|
|
"* [The Rust Reference](https://doc.rust-lang.org/reference/): 메모리 모델링과 러스트 문법을 설명하는 문서입니다.(아직 불완전하다함)"
|
|
|
|
#: src/other-resources.md:22
|
|
msgid "More specialized guides hosted on the official Rust site:"
|
|
msgstr "좀 더 전문적인 공식 가이드입니다:"
|
|
|
|
#: src/other-resources.md:24
|
|
msgid ""
|
|
"* [The Rustonomicon](https://doc.rust-lang.org/nomicon/): covers unsafe Rust,\n"
|
|
" including working with raw pointers and interfacing with other languages\n"
|
|
" (FFI).\n"
|
|
"* [Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/):\n"
|
|
" covers the new asynchronous programming model which was introduced after the\n"
|
|
" Rust Book was written.\n"
|
|
"* [The Embedded Rust Book](https://doc.rust-lang.org/stable/embedded-book/): an\n"
|
|
" introduction to using Rust on embedded devices without an operating system."
|
|
msgstr ""
|
|
"* [The Rustonomicon](https://doc.rust-lang.org/nomicon/): 안전하지 않은 러스트, FFI, raw포인터 작업을 다룹니다.\n"
|
|
"* [Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/): 러스트 북이 작성 된 이후 도입된 새로운 비동기 프로그래밍 모델을 다룹니다.\n"
|
|
"* [The Embedded Rust Book](https://doc.rust-lang.org/stable/embedded-book/): 운영체제가 없는 임베디드 장치에서의 러스트 사용법을 소개합니다."
|
|
|
|
#: src/other-resources.md:33
|
|
msgid "## Unofficial Learning Material"
|
|
msgstr "## 비공식적 학습 자료"
|
|
|
|
#: src/other-resources.md:35
|
|
msgid "A small selection of other guides and tutorial for Rust:"
|
|
msgstr "러스트에 대한 기타 안내서와 튜토리얼의 일부입니다:"
|
|
|
|
#: src/other-resources.md:37
|
|
msgid ""
|
|
"* [Learn Rust the Dangerous Way](http://cliffle.com/p/dangerust/): covers Rust\n"
|
|
" from the perspective of low-level C programmers.\n"
|
|
"* [Rust for Embedded C\n"
|
|
" Programmers](https://docs.opentitan.org/doc/ug/rust_for_c/): covers Rust from\n"
|
|
" the perspective of developers who write firmware in C.\n"
|
|
"* [Rust for professionals](https://overexact.com/rust-for-professionals/):\n"
|
|
" covers the syntax of Rust using side-by-side comparisons with other languages\n"
|
|
" such as C, C++, Java, JavaScript, and Python.\n"
|
|
"* [Rust on Exercism](https://exercism.org/tracks/rust): 100+ exercises to help\n"
|
|
" you learn Rust.\n"
|
|
"* [Ferrous Teaching\n"
|
|
" Material](https://ferrous-systems.github.io/teaching-material/index.html): a\n"
|
|
" series of small presentations covering both basic and advanced part of the\n"
|
|
" Rust language. Other topics such as WebAssembly, and async/await are also\n"
|
|
" covered.\n"
|
|
"* [Beginner's Series to\n"
|
|
" Rust](https://docs.microsoft.com/en-us/shows/beginners-series-to-rust/) and\n"
|
|
" [Take your first steps with\n"
|
|
" Rust](https://docs.microsoft.com/en-us/learn/paths/rust-first-steps/): two\n"
|
|
" Rust guides aimed at new developers. The first is a set of 35 videos and the\n"
|
|
" second is a set of 11 modules which covers Rust syntax and basic constructs.\n"
|
|
"* [Learn Rust With Entirely Too Many Linked\n"
|
|
" Lists](https://rust-unofficial.github.io/too-many-lists/): in-depth\n"
|
|
" exploration of Rust's memory management rules, through implementing a few\n"
|
|
" different types of list structures."
|
|
msgstr ""
|
|
"* [Learn Rust the Dangerous Way](http://cliffle.com/p/dangerust/): C언어 프로그래머 관점에서 러스트를 다룹니다.\n"
|
|
"* [Rust for Embedded C Programmers](https://docs.opentitan.org/doc/ug/rust_for_c/): 임베디드 C개발자(펌웨어 개발자)를 위한 러스트 가이드입니다.\n"
|
|
"* [Rust for professionals](https://overexact.com/rust-for-professionals/): 다른 언어(C/C++, Java, Python, Javascript)와의 병렬비교를 사용하여 러스트 문법을 다룹니다.\n"
|
|
"* [Rust on Exercism](https://exercism.org/tracks/rust): 러스트를 배우는데 도움이 되는 100개 이상의 연습문제\n"
|
|
"* [Ferrous Teaching Material](https://ferrous-systems.github.io/teaching-material/index.html): 러스트 언어의 기본부터 고급을 전부 다루는 일련의 작은 프레젠테이션, 웹 어셈블리, async/await 같은 부분도 함께 다룹니다.\n"
|
|
"* [Beginner's Series to Rust](https://docs.microsoft.com/en-us/shows/beginners-series-to-rust/), [Take your first steps with Rust](https://docs.microsoft.com/en-us/learn/paths/rust-first-steps/): 첫번째는 35개의 시리즈 영상이며 두번째는 러스트의 문법과 구조를 다루는 11개의 모듈 세트입니다.\n"
|
|
"* [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/): 몇가지 유형의 리스트 자료구조를 구현해보면서 러스트의 메모리 관리 규칙들을 깊이있게 탐색합니다."
|
|
|
|
#: src/other-resources.md:63
|
|
msgid ""
|
|
"Please see the [Little Book of Rust Books](https://lborb.github.io/book/) for\n"
|
|
"even more Rust books."
|
|
msgstr "[Little Book of Rust Books](https://lborb.github.io/book/)에서 더 많은 러스트 북을 확인해보세요."
|
|
|
|
#: src/credits.md:1
|
|
msgid "# Credits"
|
|
msgstr "# Credits"
|
|
|
|
#: src/credits.md:3
|
|
msgid ""
|
|
"The material here builds on top of the many great sources of Rust documentation.\n"
|
|
"See the page on [other resources](other-resources.md) for a full list of useful\n"
|
|
"resources."
|
|
msgstr "이 자료는 많은 훌륭한 러스트 문서들의 도움을 받아 작성되었습니다. 유용한 자료의 전체 목록은 [other resources](other-resources.md)에서 살펴보시기 바랍니다."
|
|
|
|
#: src/credits.md:7
|
|
msgid ""
|
|
"The material of Comprehensive Rust is licensed under the terms of the Apache 2.0\n"
|
|
"license, please see [`LICENSE`](../LICENSE) for details."
|
|
msgstr "Comprehensive Rust의 자료는 Apache 2.0 라이선스를 따릅니다. 자세한건 [`LICENSE`](../LICENSE) 확인해 보시기 바랍니다."
|
|
|
|
#: src/credits.md:10
|
|
msgid "## Rust by Example"
|
|
msgstr ""
|
|
|
|
#: src/credits.md:12
|
|
msgid ""
|
|
"Some examples and exercises have been copied and adapted from [Rust by\n"
|
|
"Example](https://doc.rust-lang.org/rust-by-example/). Please see the\n"
|
|
"`third_party/rust-by-example/` directory for details, including the license\n"
|
|
"terms."
|
|
msgstr "일부 예제와 연습문제는 [Rust by Example](https://doc.rust-lang.org/rust-by-example/)을 참조하였습니다. 라이선스 조항을 포함하여 저장소의 `third_party/rust-by-example/` 폴더를 참조하시기 바랍니다."
|
|
|
|
#: src/credits.md:17
|
|
msgid "## Rust on Exercism"
|
|
msgstr ""
|
|
|
|
#: src/credits.md:19
|
|
msgid ""
|
|
"Some exercises have been copied and adapted from [Rust on\n"
|
|
"Exercism](https://exercism.org/tracks/rust). Please see the\n"
|
|
"`third_party/rust-on-exercism/` directory for details, including the license\n"
|
|
"terms."
|
|
msgstr "일부 연습문제는 [Rust on Exercism](https://exercism.org/tracks/rust)을 참조하였습니다. 라이선스 조항을 포함하여 저장소의 `third_party/rust-on-exercism/`폴더를 참조하시기 바랍니다."
|
|
|
|
#: src/credits.md:24
|
|
msgid "## CXX"
|
|
msgstr ""
|
|
|
|
#: src/credits.md:26
|
|
msgid ""
|
|
"The [Interoperability with C++](android/interoperability/cpp.md) section uses an\n"
|
|
"image from [CXX](https://cxx.rs/). Please see the `third_party/cxx/` directory\n"
|
|
"for details, including the license terms."
|
|
msgstr "4일차 오후 강의 중 [Interoperability with C++](android/interoperability/cpp.md)에서는 [CXX](https://cxx.rs/)의 이미지를 사용하였습니다. 라이선스 조항을 포함하여 저장소의 `third_party/cxx/`폴더를 참조하시기 바랍니다."
|
|
|
|
#: src/exercises/solutions.md:1
|
|
msgid "# Solutions"
|
|
msgstr "# 해답"
|
|
|
|
#: src/exercises/solutions.md:3
|
|
msgid "You will find solutions to the exercises on the following pages."
|
|
msgstr "연습문제의 해답은 다음 페이지에서 확인할 수 있습니다."
|
|
|
|
#: src/exercises/solutions.md:5
|
|
msgid ""
|
|
"Feel free to ask questions about the solutions [on\n"
|
|
"GitHub](https://github.com/google/comprehensive-rust/discussions). Let us know\n"
|
|
"if you have a different or better solution than what is presented here."
|
|
msgstr "[깃허브](https://github.com/google/comprehensive-rust/discussions)에서 이에 대해 자유롭게 질문하시고 더 나은 솔루션이 있다면 알려주시기 바랍니다."
|
|
|
|
#: src/exercises/solutions.md:10
|
|
msgid ""
|
|
"> **Note:** Please ignore the `// ANCHOR: label` and `// ANCHOR_END: label`\n"
|
|
"> comments you see in the solutions. They are there to make it possible to\n"
|
|
"> re-use parts of the solutions as the exercises."
|
|
msgstr "> **참고:** `// ANCHOR: label`과 `// ANCHOR_END: label` 주석은 문제를 구성하기 위한 메타 주석으로 무시하시면 됩니다."
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:1
|
|
msgid "# Day 1 Morning Exercises"
|
|
msgstr "# 1일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:3
|
|
msgid "## Arrays and `for` Loops"
|
|
msgstr "## 배열과 `for`반복문"
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:5
|
|
msgid "([back to exercise](for-loops.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: transpose\n"
|
|
"fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {\n"
|
|
" // ANCHOR_END: transpose\n"
|
|
" let mut result = [[0; 3]; 3];\n"
|
|
" for i in 0..3 {\n"
|
|
" for j in 0..3 {\n"
|
|
" result[j][i] = matrix[i][j];\n"
|
|
" }\n"
|
|
" }\n"
|
|
" return result;\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: pretty_print\n"
|
|
"fn pretty_print(matrix: &[[i32; 3]; 3]) {\n"
|
|
" // ANCHOR_END: pretty_print\n"
|
|
" for row in matrix {\n"
|
|
" println!(\"{row:?}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: tests\n"
|
|
"#[test]\n"
|
|
"fn test_transpose() {\n"
|
|
" let matrix = [\n"
|
|
" [101, 102, 103], //\n"
|
|
" [201, 202, 203],\n"
|
|
" [301, 302, 303],\n"
|
|
" ];\n"
|
|
" let transposed = transpose(matrix);\n"
|
|
" assert_eq!(\n"
|
|
" transposed,\n"
|
|
" [\n"
|
|
" [101, 201, 301], //\n"
|
|
" [102, 202, 302],\n"
|
|
" [103, 203, 303],\n"
|
|
" ]\n"
|
|
" );\n"
|
|
"}\n"
|
|
"// ANCHOR_END: tests\n"
|
|
"\n"
|
|
"// ANCHOR: main\n"
|
|
"fn main() {\n"
|
|
" let matrix = [\n"
|
|
" [101, 102, 103], // <-- the comment makes rustfmt add a newline\n"
|
|
" [201, 202, 203],\n"
|
|
" [301, 302, 303],\n"
|
|
" ];\n"
|
|
"\n"
|
|
" println!(\"matrix:\");\n"
|
|
" pretty_print(&matrix);\n"
|
|
"\n"
|
|
" let transposed = transpose(matrix);\n"
|
|
" println!(\"transposed:\");\n"
|
|
" pretty_print(&transposed);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:78
|
|
msgid "### Bonus question"
|
|
msgstr "### 보너스 문제"
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:80
|
|
msgid "It requires more advanced concepts. It might seem that we could use a slice-of-slices (`&[&[i32]]`) as the input type to transpose and thus make our function handle any size of matrix. However, this quickly breaks down: the return type cannot be `&[&[i32]]` since it needs to own the data you return."
|
|
msgstr "사실 이 문제는 고급 개념이 필요합니다. 슬라이스의 슬라이스(slice-of-slices, `&[&[i32]]`)를 입력 타입으로 사용하면 모든 크기의 행렬을 처리할 수 있을것 같습니다. 하지만 실제로 해보면 금방 안된다는 걸 알 수 있습니다. 반환값을 소유해야 하기때문에 `&[&[i32]]` 반환 타입으로 사용할 수 없습니다."
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:82
|
|
msgid "You can attempt to use something like `Vec<Vec<i32>>`, but this doesn't work out-of-the-box either: it's hard to convert from `Vec<Vec<i32>>` to `&[&[i32]]` so now you cannot easily use `pretty_print` either."
|
|
msgstr "`Vec<Vec<i32>>`와 같은 타입을 사용하려고 시도할 수도 있지만 역시 쉽게 되진 않습니다. `Vec<Vec<i32>>` 타입을 `&[&[i32]]`로 변환하는 것이 어렵기 때문에 `pretty_print`을 사용하는데 어려움이 있습니다."
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:84
|
|
msgid "Once we get to traits and generics, we'll be able to use the [`std::convert::AsRef`][1] trait to abstract over anything that can be referenced as a slice."
|
|
msgstr "트레잇나 제네릭을 다루고 나면 [`std::convert::AsRef`][1] 트레잇을 사용하여 슬라이스처럼 사용될 수 있는 타입을 추상화할 수 있습니다."
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:86
|
|
msgid ""
|
|
"```rust\n"
|
|
"use std::convert::AsRef;\n"
|
|
"use std::fmt::Debug;\n"
|
|
"\n"
|
|
"fn pretty_print<T, Line, Matrix>(matrix: Matrix)\n"
|
|
"where\n"
|
|
" T: Debug,\n"
|
|
" // A line references a slice of items\n"
|
|
" Line: AsRef<[T]>,\n"
|
|
" // A matrix references a slice of lines\n"
|
|
" Matrix: AsRef<[Line]>\n"
|
|
"{\n"
|
|
" for row in matrix.as_ref() {\n"
|
|
" println!(\"{:?}\", row.as_ref());\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" // &[&[i32]]\n"
|
|
" pretty_print(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]);\n"
|
|
" // [[&str; 2]; 2]\n"
|
|
" pretty_print([[\"a\", \"b\"], [\"c\", \"d\"]]);\n"
|
|
" // Vec<Vec<i32>>\n"
|
|
" pretty_print(vec![vec![1, 2], vec![3, 4]]);\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/solutions-morning.md:113
|
|
msgid "In addition, the type itself would not enforce that the child slices are of the same length, so such variable could contain an invalid matrix."
|
|
msgstr "또한, 슬라이스 타입은 길이를 포함하지 않기 때문에 한 단계 아래의 슬라이스들이 같은 길이임을 보장할 수 없습니다. 때문에 슬라이스 타입의 변수에는 잘못된 행렬이 전달될 수 있습니다."
|
|
|
|
#: src/exercises/day-1/solutions-afternoon.md:1
|
|
msgid "# Day 1 Afternoon Exercises"
|
|
msgstr "# 1일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-1/solutions-afternoon.md:3
|
|
msgid "## Designing a Library"
|
|
msgstr "## 도서관 설계"
|
|
|
|
#: src/exercises/day-1/solutions-afternoon.md:5
|
|
msgid "([back to exercise](book-library.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-1/solutions-afternoon.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: setup\n"
|
|
"struct Library {\n"
|
|
" books: Vec<Book>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"struct Book {\n"
|
|
" title: String,\n"
|
|
" year: u16,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Book {\n"
|
|
" // This is a constructor, used below.\n"
|
|
" fn new(title: &str, year: u16) -> Book {\n"
|
|
" Book {\n"
|
|
" title: String::from(title),\n"
|
|
" year,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// This makes it possible to print Book values with {}.\n"
|
|
"impl std::fmt::Display for Book {\n"
|
|
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n"
|
|
" write!(f, \"{} ({})\", self.title, self.year)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"// ANCHOR_END: setup\n"
|
|
"\n"
|
|
"// ANCHOR: Library_new\n"
|
|
"impl Library {\n"
|
|
" fn new() -> Library {\n"
|
|
" // ANCHOR_END: Library_new\n"
|
|
" Library { books: Vec::new() }\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Library_len\n"
|
|
" //fn len(self) -> usize {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
" // ANCHOR_END: Library_len\n"
|
|
" fn len(&self) -> usize {\n"
|
|
" self.books.len()\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Library_is_empty\n"
|
|
" //fn is_empty(self) -> bool {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
" // ANCHOR_END: Library_is_empty\n"
|
|
" fn is_empty(&self) -> bool {\n"
|
|
" self.books.is_empty()\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Library_add_book\n"
|
|
" //fn add_book(self, book: Book) {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
" // ANCHOR_END: Library_add_book\n"
|
|
" fn add_book(&mut self, book: Book) {\n"
|
|
" self.books.push(book)\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Library_print_books\n"
|
|
" //fn print_books(self) {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
" // ANCHOR_END: Library_print_books\n"
|
|
" fn print_books(&self) {\n"
|
|
" for book in &self.books {\n"
|
|
" println!(\"{}\", book);\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Library_oldest_book\n"
|
|
" //fn oldest_book(self) -> Option<&Book> {\n"
|
|
" // unimplemented!()\n"
|
|
" //}\n"
|
|
" // ANCHOR_END: Library_oldest_book\n"
|
|
" fn oldest_book(&self) -> Option<&Book> {\n"
|
|
" self.books.iter().min_by_key(|book| book.year)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: main\n"
|
|
"// This shows the desired behavior. Uncomment the code below and\n"
|
|
"// implement the missing methods. You will need to update the\n"
|
|
"// method signatures, including the \"self\" parameter! You may\n"
|
|
"// also need to update the variable bindings within main.\n"
|
|
"fn main() {\n"
|
|
" let library = Library::new();\n"
|
|
"\n"
|
|
" //println!(\"Our library is empty: {}\", library.is_empty());\n"
|
|
"\n"
|
|
" let favorite_book = Book::new(\"Lord of the Rings\", 1954);\n"
|
|
" println!(\"Our favorite book {favorite_book} should go in the library\");\n"
|
|
" //library.add_book(favorite_book);\n"
|
|
" //library.add_book(Book::new(\"Alice's Adventures in Wonderland\", 1865));\n"
|
|
" //\n"
|
|
" //library.print_books();\n"
|
|
" //\n"
|
|
" //match library.oldest_book() {\n"
|
|
" // Some(book) => println!(\"My oldest book is {book}\"),\n"
|
|
" // None => println!(\"My library is empty!\"),\n"
|
|
" //}\n"
|
|
" //\n"
|
|
" //println!(\"Our library has {} books\", library.len());\n"
|
|
" for book in library.books {\n"
|
|
" println!(\"{book}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"// ANCHOR_END: main\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_library_len() {\n"
|
|
" let mut library = Library::new();\n"
|
|
" assert_eq!(library.len(), 0);\n"
|
|
" assert!(library.is_empty());\n"
|
|
"\n"
|
|
" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n"
|
|
" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", 1865));\n"
|
|
" assert_eq!(library.len(), 2);\n"
|
|
" assert!(!library.is_empty());\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_library_is_empty() {\n"
|
|
" let mut library = Library::new();\n"
|
|
" assert!(library.is_empty());\n"
|
|
"\n"
|
|
" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n"
|
|
" assert!(!library.is_empty());\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_library_print_books() {\n"
|
|
" let mut library = Library::new();\n"
|
|
" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n"
|
|
" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", 1865));\n"
|
|
" // We could try and capture stdout, but let us just call the\n"
|
|
" // method to start with.\n"
|
|
" library.print_books();\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_library_oldest_book() {\n"
|
|
" let mut library = Library::new();\n"
|
|
" assert!(library.oldest_book().is_none());\n"
|
|
"\n"
|
|
" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n"
|
|
" assert_eq!(\n"
|
|
" library.oldest_book().map(|b| b.title.as_str()),\n"
|
|
" Some(\"Lord of the Rings\")\n"
|
|
" );\n"
|
|
"\n"
|
|
" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", 1865));\n"
|
|
" assert_eq!(\n"
|
|
" library.oldest_book().map(|b| b.title.as_str()),\n"
|
|
" Some(\"Alice's Adventures in Wonderland\")\n"
|
|
" );\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-morning.md:1
|
|
msgid "# Day 2 Morning Exercises"
|
|
msgstr "# 2일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-2/solutions-morning.md:3
|
|
msgid "## Points and Polygons"
|
|
msgstr "## 점과 다각형"
|
|
|
|
#: src/exercises/day-2/solutions-morning.md:5
|
|
msgid "([back to exercise](points-polygons.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-morning.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n"
|
|
"// ANCHOR: Point\n"
|
|
"pub struct Point {\n"
|
|
" // ANCHOR_END: Point\n"
|
|
" x: i32,\n"
|
|
" y: i32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Point-impl\n"
|
|
"impl Point {\n"
|
|
" // ANCHOR_END: Point-impl\n"
|
|
" pub fn new(x: i32, y: i32) -> Point {\n"
|
|
" Point { x, y }\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn magnitude(self) -> f64 {\n"
|
|
" f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn dist(self, other: Point) -> f64 {\n"
|
|
" (self - other).magnitude()\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl std::ops::Add for Point {\n"
|
|
" type Output = Self;\n"
|
|
"\n"
|
|
" fn add(self, other: Self) -> Self::Output {\n"
|
|
" Self {\n"
|
|
" x: self.x + other.x,\n"
|
|
" y: self.y + other.y,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl std::ops::Sub for Point {\n"
|
|
" type Output = Self;\n"
|
|
"\n"
|
|
" fn sub(self, other: Self) -> Self::Output {\n"
|
|
" Self {\n"
|
|
" x: self.x - other.x,\n"
|
|
" y: self.y - other.y,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Polygon\n"
|
|
"pub struct Polygon {\n"
|
|
" // ANCHOR_END: Polygon\n"
|
|
" points: Vec<Point>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Polygon-impl\n"
|
|
"impl Polygon {\n"
|
|
" // ANCHOR_END: Polygon-impl\n"
|
|
" pub fn new() -> Polygon {\n"
|
|
" Polygon { points: Vec::new() }\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn add_point(&mut self, point: Point) {\n"
|
|
" self.points.push(point);\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn left_most_point(&self) -> Option<Point> {\n"
|
|
" self.points.iter().min_by_key(|p| p.x).copied()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn iter(&self) -> impl Iterator<Item = &Point> {\n"
|
|
" self.points.iter()\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn length(&self) -> f64 {\n"
|
|
" if self.points.is_empty() {\n"
|
|
" return 0.0;\n"
|
|
" }\n"
|
|
"\n"
|
|
" let mut result = 0.0;\n"
|
|
" let mut last_point = self.points[0];\n"
|
|
" for point in &self.points[1..] {\n"
|
|
" result += last_point.dist(*point);\n"
|
|
" last_point = *point;\n"
|
|
" }\n"
|
|
" result += last_point.dist(self.points[0]);\n"
|
|
" result\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Circle\n"
|
|
"pub struct Circle {\n"
|
|
" // ANCHOR_END: Circle\n"
|
|
" center: Point,\n"
|
|
" radius: i32,\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Circle-impl\n"
|
|
"impl Circle {\n"
|
|
" // ANCHOR_END: Circle-impl\n"
|
|
" pub fn new(center: Point, radius: i32) -> Circle {\n"
|
|
" Circle { center, radius }\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn circumference(&self) -> f64 {\n"
|
|
" 2.0 * std::f64::consts::PI * f64::from(self.radius)\n"
|
|
" }\n"
|
|
"\n"
|
|
" pub fn dist(&self, other: &Self) -> f64 {\n"
|
|
" self.center.dist(other.center)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Shape\n"
|
|
"pub enum Shape {\n"
|
|
" Polygon(Polygon),\n"
|
|
" Circle(Circle),\n"
|
|
"}\n"
|
|
"// ANCHOR_END: Shape\n"
|
|
"\n"
|
|
"impl From<Polygon> for Shape {\n"
|
|
" fn from(poly: Polygon) -> Self {\n"
|
|
" Shape::Polygon(poly)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl From<Circle> for Shape {\n"
|
|
" fn from(circle: Circle) -> Self {\n"
|
|
" Shape::Circle(circle)\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Shape {\n"
|
|
" pub fn perimeter(&self) -> f64 {\n"
|
|
" match self {\n"
|
|
" Shape::Polygon(poly) => poly.length(),\n"
|
|
" Shape::Circle(circle) => circle.circumference(),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: unit-tests\n"
|
|
"#[cfg(test)]\n"
|
|
"mod tests {\n"
|
|
" use super::*;\n"
|
|
"\n"
|
|
" fn round_two_digits(x: f64) -> f64 {\n"
|
|
" (x * 100.0).round() / 100.0\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_magnitude() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_dist() {\n"
|
|
" let p1 = Point::new(10, 10);\n"
|
|
" let p2 = Point::new(14, 13);\n"
|
|
" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_point_add() {\n"
|
|
" let p1 = Point::new(16, 16);\n"
|
|
" let p2 = p1 + Point::new(-4, 3);\n"
|
|
" assert_eq!(p2, Point::new(12, 19));\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_polygon_left_most_point() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" let p2 = Point::new(16, 16);\n"
|
|
"\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(p1);\n"
|
|
" poly.add_point(p2);\n"
|
|
" assert_eq!(poly.left_most_point(), Some(p1));\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_polygon_iter() {\n"
|
|
" let p1 = Point::new(12, 13);\n"
|
|
" let p2 = Point::new(16, 16);\n"
|
|
"\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(p1);\n"
|
|
" poly.add_point(p2);\n"
|
|
"\n"
|
|
" let points = poly.iter().cloned().collect::<Vec<_>>();\n"
|
|
" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n"
|
|
" }\n"
|
|
"\n"
|
|
" #[test]\n"
|
|
" fn test_shape_perimeters() {\n"
|
|
" let mut poly = Polygon::new();\n"
|
|
" poly.add_point(Point::new(12, 13));\n"
|
|
" poly.add_point(Point::new(17, 11));\n"
|
|
" poly.add_point(Point::new(16, 16));\n"
|
|
" let shapes = vec![\n"
|
|
" Shape::from(poly),\n"
|
|
" Shape::from(Circle::new(Point::new(10, 20), 5)),\n"
|
|
" ];\n"
|
|
" let perimeters = shapes\n"
|
|
" .iter()\n"
|
|
" .map(Shape::perimeter)\n"
|
|
" .map(round_two_digits)\n"
|
|
" .collect::<Vec<_>>();\n"
|
|
" assert_eq!(perimeters, vec![15.48, 31.42]);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"// ANCHOR_END: unit-tests\n"
|
|
"\n"
|
|
"fn main() {}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:1
|
|
msgid "# Day 2 Afternoon Exercises"
|
|
msgstr "# 2일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:3
|
|
msgid "## Luhn Algorithm"
|
|
msgstr "## 룬 알고리즘"
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:5
|
|
msgid "([back to exercise](luhn.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: luhn\n"
|
|
"pub fn luhn(cc_number: &str) -> bool {\n"
|
|
" // ANCHOR_END: luhn\n"
|
|
" let mut digits_seen = 0;\n"
|
|
" let mut sum = 0;\n"
|
|
" for (i, ch) in cc_number.chars().rev().filter(|&ch| ch != ' ').enumerate() {\n"
|
|
" match ch.to_digit(10) {\n"
|
|
" Some(d) => {\n"
|
|
" sum += if i % 2 == 1 {\n"
|
|
" let dd = d * 2;\n"
|
|
" dd / 10 + dd % 10\n"
|
|
" } else {\n"
|
|
" d\n"
|
|
" };\n"
|
|
" digits_seen += 1;\n"
|
|
" }\n"
|
|
" None => return false,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" if digits_seen < 2 {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
"\n"
|
|
" sum % 10 == 0\n"
|
|
"}\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" let cc_number = \"1234 5678 1234 5670\";\n"
|
|
" println!(\n"
|
|
" \"Is {} a valid credit card number? {}\",\n"
|
|
" cc_number,\n"
|
|
" if luhn(cc_number) { \"yes\" } else { \"no\" }\n"
|
|
" );\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: unit-tests\n"
|
|
"#[test]\n"
|
|
"fn test_non_digit_cc_number() {\n"
|
|
" assert!(!luhn(\"foo\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_empty_cc_number() {\n"
|
|
" assert!(!luhn(\"\"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
" assert!(!luhn(\" \"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_single_digit_cc_number() {\n"
|
|
" assert!(!luhn(\"0\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_two_digit_cc_number() {\n"
|
|
" assert!(luhn(\" 0 0 \"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_valid_cc_number() {\n"
|
|
" assert!(luhn(\"4263 9826 4026 9299\"));\n"
|
|
" assert!(luhn(\"4539 3195 0343 6467\"));\n"
|
|
" assert!(luhn(\"7992 7398 713\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_invalid_cc_number() {\n"
|
|
" assert!(!luhn(\"4223 9826 4026 9299\"));\n"
|
|
" assert!(!luhn(\"4539 3195 0343 6476\"));\n"
|
|
" assert!(!luhn(\"8273 1232 7352 0569\"));\n"
|
|
"}\n"
|
|
"// ANCHOR_END: unit-tests\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:98
|
|
msgid "## Strings and Iterators"
|
|
msgstr "## 문자열과 반복자"
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:100
|
|
msgid "([back to exercise](strings-iterators.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-2/solutions-afternoon.md:102
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: prefix_matches\n"
|
|
"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n"
|
|
" // ANCHOR_END: prefix_matches\n"
|
|
" let prefixes = prefix.split('/');\n"
|
|
" let request_paths = request_path\n"
|
|
" .split('/')\n"
|
|
" .map(|p| Some(p))\n"
|
|
" .chain(std::iter::once(None));\n"
|
|
"\n"
|
|
" for (prefix, request_path) in prefixes.zip(request_paths) {\n"
|
|
" match request_path {\n"
|
|
" Some(request_path) => {\n"
|
|
" if (prefix != \"*\") && (prefix != request_path) {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
" }\n"
|
|
" None => return false,\n"
|
|
" }\n"
|
|
" }\n"
|
|
" true\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: unit-tests\n"
|
|
"#[test]\n"
|
|
"fn test_matches_without_wildcard() {\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc-123\"));\n"
|
|
" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/books\"));\n"
|
|
"\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/publishers\"));\n"
|
|
"}\n"
|
|
"\n"
|
|
"#[test]\n"
|
|
"fn test_matches_with_wildcard() {\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/books\"\n"
|
|
" ));\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/bar/books\"\n"
|
|
" ));\n"
|
|
" assert!(prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/books/book1\"\n"
|
|
" ));\n"
|
|
"\n"
|
|
" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/publishers\"));\n"
|
|
" assert!(!prefix_matches(\n"
|
|
" \"/v1/publishers/*/books\",\n"
|
|
" \"/v1/publishers/foo/booksByAuthor\"\n"
|
|
" ));\n"
|
|
"}\n"
|
|
"// ANCHOR_END: unit-tests\n"
|
|
"\n"
|
|
"fn main() {}\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/solutions-morning.md:1
|
|
msgid "# Day 3 Morning Exercise"
|
|
msgstr "# 3일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-3/solutions-morning.md:3
|
|
msgid "## A Simple GUI Library"
|
|
msgstr "## 간단한 GUI 라이브러리"
|
|
|
|
#: src/exercises/day-3/solutions-morning.md:5
|
|
msgid "([back to exercise](simple-gui.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/solutions-morning.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: setup\n"
|
|
"pub trait Widget {\n"
|
|
" /// Natural width of `self`.\n"
|
|
" fn width(&self) -> usize;\n"
|
|
"\n"
|
|
" /// Draw the widget into a buffer.\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write);\n"
|
|
"\n"
|
|
" /// Draw the widget on standard output.\n"
|
|
" fn draw(&self) {\n"
|
|
" let mut buffer = String::new();\n"
|
|
" self.draw_into(&mut buffer);\n"
|
|
" println!(\"{buffer}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Label {\n"
|
|
" label: String,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Label {\n"
|
|
" fn new(label: &str) -> Label {\n"
|
|
" Label {\n"
|
|
" label: label.to_owned(),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Button {\n"
|
|
" label: Label,\n"
|
|
" callback: Box<dyn FnMut()>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Button {\n"
|
|
" fn new(label: &str, callback: Box<dyn FnMut()>) -> Button {\n"
|
|
" Button {\n"
|
|
" label: Label::new(label),\n"
|
|
" callback,\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"pub struct Window {\n"
|
|
" title: String,\n"
|
|
" widgets: Vec<Box<dyn Widget>>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"impl Window {\n"
|
|
" fn new(title: &str) -> Window {\n"
|
|
" Window {\n"
|
|
" title: title.to_owned(),\n"
|
|
" widgets: Vec::new(),\n"
|
|
" }\n"
|
|
" }\n"
|
|
"\n"
|
|
" fn add_widget(&mut self, widget: Box<dyn Widget>) {\n"
|
|
" self.widgets.push(widget);\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR_END: setup\n"
|
|
"\n"
|
|
"// ANCHOR: Window-width\n"
|
|
"impl Widget for Window {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" // ANCHOR_END: Window-width\n"
|
|
" std::cmp::max(\n"
|
|
" self.title.chars().count(),\n"
|
|
" self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),\n"
|
|
" )\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Window-draw_into\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" // ANCHOR_END: Window-draw_into\n"
|
|
" let mut inner = String::new();\n"
|
|
" for widget in &self.widgets {\n"
|
|
" widget.draw_into(&mut inner);\n"
|
|
" }\n"
|
|
"\n"
|
|
" let window_width = self.width();\n"
|
|
"\n"
|
|
" // TODO: after learning about error handling, you can change\n"
|
|
" // draw_into to return Result<(), std::fmt::Error>. Then use\n"
|
|
" // the ?-operator here instead of .unwrap().\n"
|
|
" writeln!(buffer, \"+-{:-<window_width$}-+\", \"\").unwrap();\n"
|
|
" writeln!(buffer, \"| {:^window_width$} |\", &self.title).unwrap();\n"
|
|
" writeln!(buffer, \"+={:=<window_width$}=+\", \"\").unwrap();\n"
|
|
" for line in inner.lines() {\n"
|
|
" writeln!(buffer, \"| {:window_width$} |\", line).unwrap();\n"
|
|
" }\n"
|
|
" writeln!(buffer, \"+-{:-<window_width$}-+\", \"\").unwrap();\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Button-width\n"
|
|
"impl Widget for Button {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" // ANCHOR_END: Button-width\n"
|
|
" self.label.width() + 8 // add a bit of padding\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Button-draw_into\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" // ANCHOR_END: Button-draw_into\n"
|
|
" let width = self.width();\n"
|
|
" let mut label = String::new();\n"
|
|
" self.label.draw_into(&mut label);\n"
|
|
"\n"
|
|
" writeln!(buffer, \"+{:-<width$}+\", \"\").unwrap();\n"
|
|
" for line in label.lines() {\n"
|
|
" writeln!(buffer, \"|{:^width$}|\", &line).unwrap();\n"
|
|
" }\n"
|
|
" writeln!(buffer, \"+{:-<width$}+\", \"\").unwrap();\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Label-width\n"
|
|
"impl Widget for Label {\n"
|
|
" fn width(&self) -> usize {\n"
|
|
" // ANCHOR_END: Label-width\n"
|
|
" self.label\n"
|
|
" .lines()\n"
|
|
" .map(|line| line.chars().count())\n"
|
|
" .max()\n"
|
|
" .unwrap_or(0)\n"
|
|
" }\n"
|
|
"\n"
|
|
" // ANCHOR: Label-draw_into\n"
|
|
" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n"
|
|
" // ANCHOR_END: Label-draw_into\n"
|
|
" writeln!(buffer, \"{}\", &self.label).unwrap();\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: main\n"
|
|
"fn main() {\n"
|
|
" let mut window = Window::new(\"Rust GUI Demo 1.23\");\n"
|
|
" window.add_widget(Box::new(Label::new(\"This is a small text GUI demo.\")));\n"
|
|
" window.add_widget(Box::new(Button::new(\n"
|
|
" \"Click me!\",\n"
|
|
" Box::new(|| println!(\"You clicked the button!\")),\n"
|
|
" )));\n"
|
|
" window.draw();\n"
|
|
"}\n"
|
|
"// ANCHOR_END: main\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/solutions-afternoon.md:1
|
|
msgid "# Day 3 Afternoon Exercises"
|
|
msgstr "# 3일차 오후 연습문제"
|
|
|
|
#: src/exercises/day-3/solutions-afternoon.md:3
|
|
msgid "## Safe FFI Wrapper"
|
|
msgstr "## FFI래퍼"
|
|
|
|
#: src/exercises/day-3/solutions-afternoon.md:5
|
|
msgid "([back to exercise](safe-ffi-wrapper.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-3/solutions-afternoon.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: ffi\n"
|
|
"mod ffi {\n"
|
|
" use std::os::raw::{c_char, c_int, c_long, c_ulong, c_ushort};\n"
|
|
"\n"
|
|
" // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.\n"
|
|
" #[repr(C)]\n"
|
|
" pub struct DIR {\n"
|
|
" _data: [u8; 0],\n"
|
|
" _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,\n"
|
|
" }\n"
|
|
"\n"
|
|
" // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-gnu.\n"
|
|
" #[repr(C)]\n"
|
|
" pub struct dirent {\n"
|
|
" pub d_ino: c_long,\n"
|
|
" pub d_off: c_ulong,\n"
|
|
" pub d_reclen: c_ushort,\n"
|
|
" pub d_type: c_char,\n"
|
|
" pub d_name: [c_char; 256],\n"
|
|
" }\n"
|
|
"\n"
|
|
" extern \"C\" {\n"
|
|
" pub fn opendir(s: *const c_char) -> *mut DIR;\n"
|
|
" pub fn readdir(s: *mut DIR) -> *const dirent;\n"
|
|
" pub fn closedir(s: *mut DIR) -> c_int;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"use std::ffi::{CStr, CString, OsStr, OsString};\n"
|
|
"use std::os::unix::ffi::OsStrExt;\n"
|
|
"\n"
|
|
"#[derive(Debug)]\n"
|
|
"struct DirectoryIterator {\n"
|
|
" path: CString,\n"
|
|
" dir: *mut ffi::DIR,\n"
|
|
"}\n"
|
|
"// ANCHOR_END: ffi\n"
|
|
"\n"
|
|
"// ANCHOR: DirectoryIterator\n"
|
|
"impl DirectoryIterator {\n"
|
|
" fn new(path: &str) -> Result<DirectoryIterator, String> {\n"
|
|
" // Call opendir and return a Ok value if that worked,\n"
|
|
" // otherwise return Err with a message.\n"
|
|
" // ANCHOR_END: DirectoryIterator\n"
|
|
" let path = CString::new(path).map_err(|err| format!(\"Invalid path: {err}\"))?;\n"
|
|
" // SAFETY: path.as_ptr() cannot be NULL.\n"
|
|
" let dir = unsafe { ffi::opendir(path.as_ptr()) };\n"
|
|
" if dir.is_null() {\n"
|
|
" Err(format!(\"Could not open {:?}\", path))\n"
|
|
" } else {\n"
|
|
" Ok(DirectoryIterator { path, dir })\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Iterator\n"
|
|
"impl Iterator for DirectoryIterator {\n"
|
|
" type Item = OsString;\n"
|
|
" fn next(&mut self) -> Option<OsString> {\n"
|
|
" // Keep calling readdir until we get a NULL pointer back.\n"
|
|
" // ANCHOR_END: Iterator\n"
|
|
" // SAFETY: self.dir is never NULL.\n"
|
|
" let dirent = unsafe { ffi::readdir(self.dir) };\n"
|
|
" if dirent.is_null() {\n"
|
|
" // We have reached the end of the directory.\n"
|
|
" return None;\n"
|
|
" }\n"
|
|
" // SAFETY: dirent is not NULL and dirent.d_name is NUL\n"
|
|
" // terminated.\n"
|
|
" let d_name = unsafe { CStr::from_ptr((*dirent).d_name.as_ptr()) };\n"
|
|
" let os_str = OsStr::from_bytes(d_name.to_bytes());\n"
|
|
" Some(os_str.to_owned())\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Drop\n"
|
|
"impl Drop for DirectoryIterator {\n"
|
|
" fn drop(&mut self) {\n"
|
|
" // Call closedir as needed.\n"
|
|
" // ANCHOR_END: Drop\n"
|
|
" if !self.dir.is_null() {\n"
|
|
" // SAFETY: self.dir is not NULL.\n"
|
|
" if unsafe { ffi::closedir(self.dir) } != 0 {\n"
|
|
" panic!(\"Could not close {:?}\", self.path);\n"
|
|
" }\n"
|
|
" }\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: main\n"
|
|
"fn main() -> Result<(), String> {\n"
|
|
" let iter = DirectoryIterator::new(\".\")?;\n"
|
|
" println!(\"files: {:#?}\", iter.collect::<Vec<_>>());\n"
|
|
" Ok(())\n"
|
|
"}\n"
|
|
"// ANCHOR_END: main\n"
|
|
"```"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/solutions-morning.md:1
|
|
msgid "# Day 4 Morning Exercise"
|
|
msgstr "# 4일차 오전 연습문제"
|
|
|
|
#: src/exercises/day-4/solutions-morning.md:3
|
|
msgid "## Dining Philosophers"
|
|
msgstr "## 식사하는 철학자들"
|
|
|
|
#: src/exercises/day-4/solutions-morning.md:5
|
|
msgid "([back to exercise](dining-philosophers.md))"
|
|
msgstr ""
|
|
|
|
#: src/exercises/day-4/solutions-morning.md:7
|
|
msgid ""
|
|
"```rust\n"
|
|
"// Copyright 2022 Google LLC\n"
|
|
"//\n"
|
|
"// Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
|
"// you may not use this file except in compliance with the License.\n"
|
|
"// You may obtain a copy of the License at\n"
|
|
"//\n"
|
|
"// http://www.apache.org/licenses/LICENSE-2.0\n"
|
|
"//\n"
|
|
"// Unless required by applicable law or agreed to in writing, software\n"
|
|
"// distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
|
"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
|
"// See the License for the specific language governing permissions and\n"
|
|
"// limitations under the License.\n"
|
|
"\n"
|
|
"// ANCHOR: Philosopher\n"
|
|
"use std::sync::mpsc;\n"
|
|
"use std::sync::{Arc, Mutex};\n"
|
|
"use std::thread;\n"
|
|
"use std::time::Duration;\n"
|
|
"\n"
|
|
"struct Fork;\n"
|
|
"\n"
|
|
"struct Philosopher {\n"
|
|
" name: String,\n"
|
|
" // ANCHOR_END: Philosopher\n"
|
|
" left_fork: Arc<Mutex<Fork>>,\n"
|
|
" right_fork: Arc<Mutex<Fork>>,\n"
|
|
" thoughts: mpsc::SyncSender<String>,\n"
|
|
"}\n"
|
|
"\n"
|
|
"// ANCHOR: Philosopher-think\n"
|
|
"impl Philosopher {\n"
|
|
" fn think(&self) {\n"
|
|
" self.thoughts\n"
|
|
" .send(format!(\"Eureka! {} has a new idea!\", &self.name))\n"
|
|
" .unwrap();\n"
|
|
" }\n"
|
|
" // ANCHOR_END: Philosopher-think\n"
|
|
"\n"
|
|
" // ANCHOR: Philosopher-eat\n"
|
|
" fn eat(&self) {\n"
|
|
" // ANCHOR_END: Philosopher-eat\n"
|
|
" println!(\"{} is trying to eat\", &self.name);\n"
|
|
" let left = self.left_fork.lock().unwrap();\n"
|
|
" let right = self.right_fork.lock().unwrap();\n"
|
|
"\n"
|
|
" // ANCHOR: Philosopher-eat-end\n"
|
|
" println!(\"{} is eating...\", &self.name);\n"
|
|
" thread::sleep(Duration::from_millis(10));\n"
|
|
" }\n"
|
|
"}\n"
|
|
"\n"
|
|
"static PHILOSOPHERS: &[&str] =\n"
|
|
" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n"
|
|
"\n"
|
|
"fn main() {\n"
|
|
" // ANCHOR_END: Philosopher-eat-end\n"
|
|
" let (tx, rx) = mpsc::sync_channel(10);\n"
|
|
"\n"
|
|
" let forks = (0..PHILOSOPHERS.len())\n"
|
|
" .map(|_| Arc::new(Mutex::new(Fork)))\n"
|
|
" .collect::<Vec<_>>();\n"
|
|
"\n"
|
|
" for i in 0..forks.len() {\n"
|
|
" let tx = tx.clone();\n"
|
|
" let mut left_fork = forks[i].clone();\n"
|
|
" let mut right_fork = forks[(i + 1) % forks.len()].clone();\n"
|
|
"\n"
|
|
" // To avoid a deadlock, we have to break the symmetry\n"
|
|
" // somewhere. This will swap the forks without deinitializing\n"
|
|
" // either of them.\n"
|
|
" if i == forks.len() - 1 {\n"
|
|
" std::mem::swap(&mut left_fork, &mut right_fork);\n"
|
|
" }\n"
|
|
"\n"
|
|
" let philosopher = Philosopher {\n"
|
|
" name: PHILOSOPHERS[i].to_string(),\n"
|
|
" thoughts: tx,\n"
|
|
" left_fork,\n"
|
|
" right_fork,\n"
|
|
" };\n"
|
|
"\n"
|
|
" thread::spawn(move || {\n"
|
|
" for _ in 0..100 {\n"
|
|
" philosopher.eat();\n"
|
|
" philosopher.think();\n"
|
|
" }\n"
|
|
" });\n"
|
|
" }\n"
|
|
"\n"
|
|
" drop(tx);\n"
|
|
" for thought in rx {\n"
|
|
" println!(\"{thought}\");\n"
|
|
" }\n"
|
|
"}\n"
|
|
"```"
|
|
msgstr ""
|