• 3881e96908b8dd336d01f9f021fd5fd6?s=80&d=mm

    Changes from Jung

    Jung - almost 4 years ago (Sep 17, 2015, 3:09 AM)
    링크수정
  • Changes have been accepted Merged
      Looks like something's not quite right here.. We've been notified of the issue, and will get back to you soon.
      readme
      # 1. 소개(Introduction) - 100%

      환영합니다! 이 책은 당신에게 [Rust 프로그래밍 언어][rust]에 대해 알려줄 것입니다. Rust는 세가지 목표(안전성, 속도, 병행성)에 초점을 맞춘 시스템 프로그래밍 언어입니다. Rust는 가비지 콜렉터 없이 이러한 목표를 달성하고 있고, 때문에 다른 언어들이 그다지 훌륭하지 못한 몇 가지 부분에서 강세를 보입니다. 예를 들어 다른 언어에 내장(_embedding_)시키는 일, 특정한 공간/시간 제약을 갖는 프로그램을 작성하는 일, 장치 드라이버나 운영 체제 등의 로우 레벨 코드를 작성하는 일 등이죠. Rust는 컴파일 타임에 이루어지는 몇 가지 안정성 체크를 통해 런타임 오버헤드를 발생시키지 않으면서도 이러한 목표를 가진 현존하는 언어들보다 뛰어난 성과를 보여줍니다. 또한, Rust는 고수준 언어들이 제공하는 것과 비슷하게 느껴지는 추상화를 제공하면서도 '무비용 추상화_zero-cost abstraction_'을 달성하고자 합니다. 그러면서도, 많은 로우 레벨 언어들처럼 정밀한 제어도 가능케 하죠.

      [rust]: http://rust-lang.org

      “Rust 프로그래밍 언어”는 여덟 단원으로 구분됩니다. 이 소개는 그 중 첫번째고, 나머지는 다음과 같습니다.

      * [시작하기(Getting started)][gs] - Rust 개발을 위한 컴퓨터 환경 구축.
      * [Rust 배우기(Learn Rust)][lr] - 작은 프로젝트를 통한 Rust 프로그래밍의 학습.
      * [효과적인 Rust(Effective Rust)][er] - 훌륭한 Rust 코드를 작성하기 위한 더 높은 수준의 개념들.
      * [문법과 의미(Syntax and Semantics)][ss] - 조그만 조각들로 쪼개서 살펴보는 Rust의 세세한 부분들.
      * [실험적 Rust(Nightly Rust)][nr] - 아직 안정적인 빌드에 포함되지 않은 최신 기능들.
      * [용어 해설(Glossary)][gl] - 책에서 사용된 용어들의 참조.
      * [학문적 연구(Academic Research)][ar] - Rust에 영향을 준 문헌.

      [gs]: getting-started.md
      [lr]: learn-rust.md
      [er]: effective-rust.md
      [ss]: syntax-and-semantics.md
      [nr]: nightly-rust.md
      [gl]: glossary.md
      [ar]: academic-research.md

      이 글을 읽은 후, 'Rust 배우기'나 '문법과 의미' 둘 중 하나로 넘어가길 추천드립니다. 프로젝트를 하나 붙잡고 집중하고 싶다면 'Rust 배우기'를, 다음으로 넘어가기 전에 개념을 하나씩 완전히 익히는 것을 선호하신다면 '문법과 의미'를 선택하면 됩니다. 이 두 부분 사이엔 많은 다리들이 연결되어 있지요.

      ### 기여

      이 책을 생성하는 원본 파일들은 Github에서 찾을 수 있습니다.
      [github.com/rust-lang/rust/tree/master/src/doc/trpl](https://github.com/rust-lang/rust/tree/master/src/doc/trpl)

      ## Rust에 대한 간략한 소개

      Rust는 당신이 흥미를 가질 만한 언어일까요? Rust의 장점 몇 가지를 보여주기 위해 짧은 예제 코드 몇 개를 살펴보죠.

      Rust를 다른 어떤 언어와도 다른 것으로 만드는 주요 개념은 바로 '소유권'이라고 합니다. 다음 짧은 예제를 한 번 보죠.

      ```rust
      fn main() {
      let mut x = vec!["Hello", "world"];
      }
      ```

      이 프로그램은 `x`라는 이름의 [변수 바인딩][var]을 생성합니다. 이 바인딩의 값은 `Vec` 또는 '벡터'입니다. 우리는 이것을 표준 라이브러리에 정의되어 있는 [매크로][macro]를 통해서 만들었습니다. 이 매크로는 `vec`이라고 하는데, 모든 매크로는 `!`과 함께 실행됩니다. 이것은 Rust의 기본 원칙 중 하나를 따른 것입니다. "모든 것을 명시적으로 만들어라." 함수에 비교했을 때 매크로는 훨씬 더 복잡한 작업들을 할 수 있고, 따라서 둘은 외형적으로 구분됩니다. 또한 `!`은 파싱 과정을 도우며, 중요한 도구들을 작성하기 쉽도록 해 줍니다. 역시 중요한 점이죠.

      우리는 `mut`를 사용해서 `x`를 변경 가능한 변수_mutable variable_로 만들었습니다. Rust에서는 변수 바인딩은 기본적으로 변경할 수 없습니다_immutable_. 우리는 나중에 이 벡터를 변경하는 예제도 살펴보죠.

      타입을 명시적으로 기술하지 않았다는 것도 주목할만한 점이죠. Rust는 정적 타입 언어지만, 타입을 꼭 명시적으로 적을 필요가 없습니다. 정적 타이핑의 강력함과 타입을 장황하게 적어야 하는 불편함 사이에서 균형을 잡기 위해, Rust는 타입 추론(type inference) 기능을 가지고 있습니다.

      Rust는 힙 할당보다 스택 할당을 선호합니다. `x`는 스택 위에 바로 할당됩니다. 그러나, `Vec` 타입은 벡터의 요소들을 저장하기 위한 공간을 힙에 할당합니다. 만약 무슨 차이인지 모르겠다면, 일단 여기에서는 무시해도 좋습니다. 아니면 [‘스택과 힙’][heap] 문서를 참고하셔도 좋구요. 시스템 프로그래밍 언어로서 Rust는 메모리를 어떻게 할당할 것인가에 대한 제어권을 사용자에게 제공합니다. 그러나 우리는 이제 막 시작하는 참이고, 이게 그렇게 중요한 일은 아니겠죠.

      [var]: variable-bindings.htmlmd
      [macro]: macros.htmlmd
      [heap]: the-stack-and-the-heap.htmlmd

      앞서 '소유권'이 Rust의 새로운 주요 개념이라고 언급한 바 있습니다. Rust 어법에서, `x`는 그 벡터를 '소유합니다'. 이는 `x`가 유효 범위(Scope)의 바깥으로 나갈 때, 그 벡터의 메모리가 해제된다는 것을 의미합니다. Rust 컴파일러는 이 과정을 쓰레기 수집(Garbage collection) 등의 방법을 통하지 않고 결정론적으로 수행합니다. 다른 말로 설명하면, Rust에서 여러분은 `malloc`이나 `free` 같은 함수를 직접 호출하지 않습니다. 컴파일러는 어느 시점에 메모리를 할당하고 해제해야 하는지를 정적으로 결정하고, 그런 호출을 스스로 집어넣습니다. 인간은 실수하는 동물이지만, 컴파일러는 절대 까먹지 않습니다.

      우리 예제에 다른 한 줄을 추가해 봅시다.

      ```rust
      fn main() {
      let mut x = vec!["Hello", "world"];

      let y = &x[0];
      }
      ```

      우리는 또 다른 바인딩 `y`를 도입했습니다. 여기에서 `y`는 벡터의 첫 번째 요소를 가리키는 '참조' 입니다. Rust의 참조는 다른 언어들의 포인터와 비슷하지만, 컴파일 시간에 수행되는 추가적인 안전성 검사를 가지고 있습니다. 참조들은 그들이 가리키는 것을 소유하는 대신 [‘빌림’][borrowing]으로써 소유권 시스템과 소통합니다. 참조는 유효 범위 바깥으로 나가더라도 자신이 가리키고 있던 메모리를 해제하지 않는다는 차이점을 가지고 있습니다. 만약 그렇지 않았다면 같은 메모리에 대한 두 번의 해제가 일어나겠죠. 그게 영 좋지 않은 일이란건 명백하구요!
      [borrowing]: references-and-borrowing.htmlmd

      세 번째 줄을 추가해 봅시다. 겉보기에는 별 문제 없어 보이는 코드입니다만, 이는 컴파일러 오류를 유발합니다.

      ```rust,ignore
      fn main() {
      let mut x = vec!["Hello", "world"];

      let y = &x[0];

      x.push("foo");
      }
      ```
      `push`는 벡터의 메서드로 또 다른 요소 하나를 벡터의 끝에 추가합니다. 이 프로그램을 컴파일하려 시도하면 다음과 같은 오류를 얻게 됩니다.

      ```text
      error: cannot borrow `x` as mutable because it is also borrowed as immutable
      x.push("foo");
      ^
      note: previous borrow of `x` occurs here; the immutable borrow prevents
      subsequent moves or mutable borrows of `x` until the borrow ends
      let y = &x[0];
      ^
      note: previous borrow ends here
      fn main() {

      }
      ^
      ```

      휴! 때때로 Rust 컴파일러는 오류에 대해 상당히 자세히 알려줍니다. 그리고 이번이 바로 그런 때로군요. 오류 메시지에 따르면, 우리가 변수 바인딩을 변경할 수 있도록 만들음에도 불구하고 여전히 `push`를 호출할 수 없습니다. 이것은 우리가 벡터의 한 요소를 가리키는 참조 `y`를 이미 가지고 있기 때문입니다. 다른 참조가 가리키고 있는 무엇인가를 변경하는 것은, 우리가 그 참조를 무효화할 수도 있기 때문에 위험한 행동입니다. 이 경우를 구체적으로 살펴보면, 벡터를 생성할 때, 딱 두 개의 요소를 담을 수 있는 메모리만이 할당되었을 수도 있겠죠. 여기에 세 번째 요소를 추가한다는 것은 세 요소를 모두를 담을 수 있는 메모리 공간을 새롭게 할당하고, 기존 값들을 복사하고, 그 메모리를 가리키도록 내부 포인터를 업데이트하는 것을 의미합니다. 이러한 일련의 동작은 별 문제없이 작동합니다. 문제는 그 과정에서 `y`는 업데이트되지 않고, 따라서 '댕글링 포인터_dangling pointer_'를 갖게 된다는 것입니다. 좋지 않죠. 이 경우 `y`를 어떤 방식으로든 사용한다면 에러가 날 것이므로, 컴파일러가 우리를 위해서 잡아준 것입니다.

      어떻게 이 문제를 해결할 수 있을까요? 두 가지 접근법을 취할 수 있습니다. 첫 번째 방법은 참조를 사용하는 대신 복제본을 만드는 것입니다.

      ```rust
      fn main() {
      let mut x = vec!["Hello", "world"];

      let y = x[0].clone();

      x.push("foo");
      }
      ```

      Rust는 기본적으로 [move semantics][move]을 따릅니다. 따라서 만약 어떤 데이터의 복제본을 만들고 싶다면, `clone()` 메소드를 호출합니다. 이 예제에서, `y`는 더 이상 `x`에 저장된 벡터를 가리키는 참조가 아니고, 그 첫 번째 요소의 복제본인 `"Hello"`입니다. 이제 참조가 존재하지 않으므로, `push()`는 잘 동작하겠죠.

      [move]: ownership.html#move-semantics

      만약 정말로 참조를 사용하고 싶다면, 또 다른 선택지가 필요합니다. 어떤 참조에 대한 변경이 시행되기 전에 반드시 그것이 유효 범위를 벗어나도록 만드는거죠. 다음과 같이 말입니다.


      ```rust
      fn main() {
      let mut x = vec!["Hello", "world"];

      {
      let y = &x[0];
      }

      x.push("foo");
      }
      ```

      우리는 추가적인 중괄호 쌍을 이용해 안쪽에 새로운 유효 범위를 만들었습니다. `y`는 우리가 `push()` 를 호출하기 전에 유효 범위를 벗어날 것이므로 문제는 발생하지 않겠죠.


      이렇듯 소유권 시스템은 단지 댕글링 포인터뿐만 아니라, 반복자 무효화_iterator invalidation_, 동시성_concurrency_ 등 연관된 모든 문제점들을 예방하는데에 유용합니다.