Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

새로운 언어 기능 구현하기

컴파일러에 중요한 새 기능을 구현하려면 모든 것이 순조롭게 진행되도록 이 절차를 거쳐야 합니다.

참고: 이 절은 언어 기능에 관한 것입니다. 다른 절차를 따르는 라이브러리 기능에는 해당하지 않습니다.

언어 변경을 제안하는 방법은 Rust 언어 설계 팀의 절차도 참고하십시오.

새로운 기능

Rust는 강력한 하위 호환성을 보장합니다. 따라서 새로운 기능을 stable Rust에 곧바로 구현할 수는 없습니다. 대신 stable, 베타, nightly라는 3개의 릴리스 채널을 운영합니다. Rust의 기차 릴리스 모델에 관한 자세한 내용은 The Rust Book을 참고하십시오.

  • Stable: 일반적인 용도로 쓰는 최신 안정 릴리스입니다.
  • 베타: 다음 릴리스입니다(6주 이내에 stable이 됩니다).
  • Nightly: 저장소의 main 브랜치를 따릅니다. 불안정 기능을 사용하도록 의도된 유일한 채널이며, 불안정 기능은 옵트인 방식의 기능 게이트를 통해 사용합니다.

호환성을 깨는 변경

호환성을 깨는 변경은 개발 가이드에 전용 절이 따로 있습니다.

주요 변경

“What proposal approval do I need?”를 참고하십시오. 해당 문서에 나오는 용어의 정의는 “Proposals”를 참고하십시오.

확신이 서지 않을 때는 Zulip에서 질문하십시오. 결국 병합되지 않을 PR에 많은 노력을 쏟는다면 안타까운 일입니다!

@rfcbot FCP 절차

변경 사항이 작고, 논란의 여지가 없고, 호환성을 깨지 않으며, 사용자가 관찰할 수 있는 어떤 방식으로도 stable 언어에 영향을 주지 않고, 새로운 불안정 기능도 추가하지 않는다면, PR을 작성하고 해당 코드 영역을 잘 아는 사람에게 r+를 받는 것만으로 충분합니다. 그렇지 않다면 더 많은 절차가 필요합니다. 컴파일러 내부 작업이라 하더라도 팀의 나머지 구성원과 합의하지 않은 채 논란의 여지가 있는 변경을 밀어붙이는 것은 좋지 않습니다(“분산 시스템“의 관점에서는 여러분이 모르는 부분을 망가뜨리지 않기 위해서이고, 사회적 관점에서는 PR 분쟁을 피하기 위해서입니다).

팀의 합의가 필요한 변경에는 최종 의견 수렴 기간(final comment period, FCP)을 제안하는 절차를 사용합니다. 해당 팀의 구성원이 아니라면(따라서 @rfcbot 권한이 없다면) 팀 구성원에게 FCP를 시작해 달라고 요청하십시오. 그 구성원 자신에게 우려 사항이 없다면 FCP를 시작해 주어야 합니다.

FCP 절차는 합의가 필요한 경우에만 필요합니다. 변경 사항에 대해 합의를 요구하는 절차가 없고 누구도 문제 삼지 않으리라 생각된다면 r+만으로 진행해도 괜찮습니다. 예를 들어 컴파일러 개발이나 표준 라이브러리에서 사용할 목적으로, 예약된 컴파일러 내부 rustc_ 네임스페이스에 속한 불안정한 명령줄 플래그나 속성을 추가하거나 수정하는 일은 nightly 생태계에서 널리 쓰이지 않으리라 예상되는 한 FCP 없이 진행해도 괜찮습니다. 일부 팀에는 이런 상황에서 사용하는 더 가벼운 절차가 있습니다. 예를 들어 컴파일러 팀은 완전한 합의 없이도 지지와 피드백을 모으는 가벼운 방법으로 주요 변경 제안(Major Change Proposal, MCP)을 제출할 것을 권장합니다.

FCP를 제안하기 위해 구현이 r+를 받을 만큼 완전히 준비되어 있을 필요는 없지만, 여러분이 무엇을 말하는지 사람들이 알 수 있도록 적어도 개념 증명(proof of concept) 정도는 갖추는 것이 일반적으로 좋습니다.

FCP가 제안되면 팀의 모든 구성원이 FCP를 승인해야 합니다. 모든 구성원이 승인하면 누구나 의견을 남길 수 있는 10일간의 “최종 의견 수렴 기간”(이름도 여기에서 유래했습니다)이 이어지며, 이 기간에 아무런 우려도 제기되지 않으면 해당 PR/이슈는 FCP 승인을 받습니다.

기능 작성의 실무 절차

기능을 제대로 동작하도록 구현하려면 거쳐야 할 몇 가지 “절차상의” 관문이 있습니다.

경고 주기

경우에 따라 기능이나 버그 수정이 일부 엣지 케이스에서 기존 프로그램을 깨뜨릴 수 있습니다. 그런 경우에는 crater 실행으로 영향을 평가하고, 필요하다면 에디션 게이트 린트에 사용되는 것과 유사한 향후 호환성 린트를 추가하는 것이 좋습니다.

안정성

우리는 Rust의 안정성을 중요하게 여깁니다. stable에서 동작하고 실행되는 코드는 (대부분) 깨져서는 안 됩니다. 그렇기 때문에 우리는 팀의 합의와 코드 리뷰만으로 기능을 세상에 공개하고 싶지 않습니다. nightly에서 해당 기능을 사용하며 실제 경험을 쌓고자 하며, 그 경험을 바탕으로 기능을 변경하고 싶을 수도 있습니다.

이를 가능하게 하려면 사용자가 실수로 새 기능에 의존하지 않도록 해야 합니다. 그렇지 않으면, 특히 실험에 시간이 걸리거나 지연되어 기능이 릴리스 열차를 타고 stable에 도달하게 되면, 해당 기능은 사실상 안정화된 상태가 되어 사람들의 코드를 깨뜨리지 않고는 변경할 수 없게 됩니다.

이를 위해 모든 새 기능이 기능 게이트로 보호되도록 합니다. 즉, 새 기능은 기능 게이트(#[feature(foo)])를 활성화하지 않으면 사용할 수 없으며, 기능 게이트는 stable/베타 컴파일러에서는 활성화할 수 없습니다. 기술적인 세부 사항은 코드에서의 안정성 절을 참고하십시오.

결국 기능을 사용하며 충분한 경험을 쌓고, 필요한 변경을 마치고, 만족스러운 상태가 되면, 여기에 설명된 안정화 절차를 통해 기능을 세상에 공개합니다. 그때까지 기능은 확정된 것이 아닙니다. 기능의 모든 부분이 변경될 수 있으며, 기능이 완전히 다시 작성되거나 제거될 수도 있습니다. 기능은 오랜 기간 불안정한 상태로 변경 없이 유지되었다는 이유만으로 지위를 보장받지 않습니다.

추적 이슈

불안정 기능의 상태, nightly에서 그 기능을 사용하며 얻는 경험, 그리고 기능의 안정화를 가로막는 블록 요인이 되는 우려 사항을 추적하기 위해 모든 기능 게이트에는 추적 이슈가 필요합니다. 해당 기능과 관련된 이슈와 PR을 만들 때는 이 추적 이슈를 참조하고, 기능의 진행 상황에 관한 업데이트가 있으면 추적 이슈에 게시하십시오.

승인된 RFC나 승인된 언어 실험의 일부인 기능의 경우, 해당 추적 이슈를 사용하십시오.

그 밖의 기능에 대해서는 해당 기능의 추적 이슈를 만드십시오. 이슈 제목은 “Tracking issue for YOUR FEATURE“여야 합니다. “Tracking Issue”(추적 이슈) 이슈 템플릿을 사용하십시오.

언어 실험

컴파일러에 반영되려면, 언어에 사용자가 볼 수 있는 영향을 미치는 기능은 (불안정한 기능이라 하더라도) 승인된 RFC의 일부이거나 승인된 언어 실험이어야 합니다.

새로운 언어 실험을 제안하려면 rust-lang/rust에 동기와 의도한 해결책을 설명하는 이슈를 여십시오. 제안이 승인되면 이 이슈가 실험의 추적 이슈가 되므로, 추적 이슈 템플릿을 사용하면서 이러한 다른 세부 사항도 함께 포함하십시오. 해당 이슈를 lang 팀에 지명하고 @rust-lang/lang과 @rust-lang/lang-advisors를 CC하십시오. 실험이 승인되면 추적 이슈에 B-experimental 표시가 붙습니다.

언어 실험과 관련된 기능 플래그는 해당 기능에 대한 RFC가 승인될 때까지 incomplete로 표시해야 합니다.

코드에서의 안정성

새로운 불안정 기능을 구현하려면 아래 단계를 따라야 합니다.

  1. 추적 이슈를 열거나 확인하십시오. 승인된 RFC나 승인된 언어 실험의 일부인 기능의 경우, 해당 추적 이슈를 사용하십시오.

    추적 이슈에 C-tracking-issue 레이블과 관련 F-feature_name 레이블을 붙이십시오(필요하면 해당 레이블을 새로 만드십시오).

  2. 기능 게이트의 이름을 정하십시오(RFC가 있는 경우 RFC에 있는 이름을 사용하십시오).

  3. rustc_span/src/symbol.rs의 Symbols {...} 블록에 기능 이름을 추가하십시오.

    이 블록은 알파벳 순서를 유지해야 한다는 점에 유의하십시오.

  4. rustc_feature/src/unstable.rs의 불안정 declare_features 블록에 기능 게이트 선언을 추가하십시오.

    /// description of feature
    (unstable, $feature_name, "CURRENT_RUSTC_VERSION", Some($tracking_issue_number))

    아직 추적 이슈를 열지 않았다면(예를 들어 기능이 수락될 가능성이 있는지 먼저 피드백을 받고 싶은 경우) 임시로 None을 사용할 수 있습니다. 다만 PR이 병합되기 전에 반드시 갱신하십시오!

    예를 들면 다음과 같습니다.

    /// Allows defining identifiers beyond ASCII.
    (unstable, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467)),

    기능의 타입을 incomplete로 설정하면 해당 기능을 미완성으로 표시할 수 있으며, 이 경우 기본적으로 경고를 내는 incomplete_features 린트가 발생합니다.

    /// Allows deref patterns.
    (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)),

    언어 실험과 관련된 기능 플래그는 해당 기능에 대한 RFC가 수락될 때까지 incomplete로 표시해야 합니다.

    의미적 병합 충돌을 피하려면 1.70이나 다른 명시적인 버전 번호 대신 CURRENT_RUSTC_VERSION을 사용하십시오.

  5. 기능 게이트가 설정되지 않은 경우에는 새 기능을 사용하지 못하도록 막으십시오. 컴파일러의 대부분의 위치에서 tcx.features().$feature_name() 표현식으로 이를 확인할 수 있습니다.

    기능 게이트가 설정되지 않은 경우, 상황에 맞게 기능 도입 이전의 동작을 유지하거나 오류를 발생시켜야 합니다. 오류에는 일반적으로 rustc_session::errors::feature_err를 사용해야 합니다. 오류를 추가하는 예시는 #81015를 참고하십시오.

    새 구문을 도입하는 기능에는 대신 확장 전 게이팅을 사용해야 합니다. 파싱 중에 새 구문이 파싱되면 self.psess.gated_spans.gate(sym::my_feature, span)를 통해 현재 크레이트의 GatedSpans에 심볼을 삽입해야 합니다.

    게이트된 스팬에 삽입된 후에는 실제로 기능을 거부하는 rustc_ast_passes::feature_gate::check_crate 함수에서 해당 스팬을 검사해야 합니다. 정확히 어떻게 게이트되는지는 기능의 구체적인 종류에 따라 다르지만, 대부분 gate_all!() 매크로를 사용하게 됩니다.

  6. tests/ui/feature-gates/feature-gate-$feature_name.rs를 만들어 기능 게이트 없이는 해당 기능을 사용할 수 없음을 확인하는 테스트를 추가하십시오. ./x test tests/ui/feature-gates/ --bless를 실행하면 대응하는 .stderr 파일을 생성할 수 있습니다.

  7. src/doc/unstable-book/src/language-features/$feature_name.md에 불안정 기능 책(unstable book)의 섹션을 추가하십시오.

  8. 새 기능에 대한 테스트를 많이 작성하십시오. 가능하면 tests/ui/$feature_name/에 작성하십시오. 테스트가 없는 PR은 수락되지 않습니다!

  9. PR 리뷰를 받고 병합하십시오. 이제 Rust에 기능을 성공적으로 구현했습니다!

불안정 컴파일러 플래그 추가하기

컴파일러 내부 실험, 진단, 디버깅 보조 기능, 임시 구현 제어에는 언어 기능 게이트보다 -Z 플래그가 더 적합할 수 있습니다. 그래도 사용자에게 노출되는 언어 기능, 특히 크레이트가 소스 코드에서 옵트인해야 하는 경우에는 여전히 기능 게이트를 우선적으로 사용해야 합니다.

새 플래그를 추가하기 전에 컴파일러 팀의 컴파일러 플래그 지침을 확인하십시오. 일부 플래그, 특히 nightly 사용자 워크플로에서 직접 사용하거나 더 넓은 생태계에서 사용하도록 의도된 플래그는 구현 PR에 대한 r+ 외에 추가 승인이 필요할 수 있습니다.

새 -Z 플래그를 추가할 때는 다음을 따르십시오.

  1. compiler/rustc_session/src/options.rs의 UnstableOptions에 옵션을 추가하십시오. 옵션 이름은 구조체에서는 snake_case로 작성되며, 명령줄에서는 kebab-case로 노출됩니다. 파서, 기본값, 의존성 추적 마커를 신중하게 선택하십시오. 진단이나 디버깅 출력에만 영향을 주는 플래그에는 [UNTRACKED]를 사용하고, 플래그를 변경했을 때 컴파일 결과가 달라질 수 있는 경우에는 [TRACKED]를 사용하십시오.

  2. compiler/rustc_interface/src/tests.rs의 옵션 테스트를 갱신하십시오. options.rs에는 UnstableOptions 목록 근처에 함께 동기화해야 하는 파일들에 대한 짧은 체크리스트도 있습니다.

  3. 옵션은 sess.opts.unstable_opts.$flag_name 또는 tcx.sess.opts.unstable_opts.$flag_name을 통해 사용하십시오. bootstrap이나 rustdoc이 내부적으로 플래그를 전달해야 한다면, 같은 변경의 일부로 관련 명령 구성 코드에 플래그가 전달되도록 연결하십시오.

  4. 플래그가 제어하는 동작에 초점을 맞춘 테스트를 추가하십시오. 예를 들어, UI 테스트에서는 //@ compile-flags: -Zyour-flag를 사용할 수 있으며, rustdoc 동작에 대한 테스트는 보통 tests/rustdoc에 둡니다.

  5. 플래그가 nightly rustc나 rustdoc 사용자를 위한 것이라면, src/doc/unstable-book/src/compiler-flags 아래의 unstable book에 문서화하십시오.

PR을 열기 전에 ./x test tidy와 새 플래그를 검증하는 좁은 범위의 테스트 스위트를 실행하십시오.

테스트 요청

구현이 완료되면 해당 기능은 nightly 사용자가 사용할 수 있게 되지만, 아직 stable Rust에 포함되지는 않습니다. 이때가 Rust 공식 블로그에 블로그 글을 작성하여 “테스트 요청“을 하기에 좋은 시점입니다.

이전에 작성된 이러한 블로그 글로는 다음과 같은 것들이 있습니다:

  1. GAT 안정화를 위한 추진
  2. Rust 2024에서의 impl Trait 변경 사항
  3. 비동기 클로저 MVP: 테스트 요청!

또는 This Week in Rust에도 이를 위한 섹션이 있습니다. 이 섹션이 사용된 예시는 다음과 같습니다:

어떤 방법을 선택할지는 언어 변경이 얼마나 중대한지에 따라 달라질 수 있지만, This Week in Rust 섹션은 Rust 공식 블로그의 전용 글보다 눈에 덜 띌 수 있다는 점에 유의하십시오.

다듬기

사용자에게 잘 다듬어진 경험을 제공한다는 것은 단순히 rustc에 기능을 구현하는 것 이상을 의미합니다. 우리가 배포하는 모든 도구와 자료를 고려해야 합니다. 이 작업에는 다음이 포함됩니다:

  • Rust Reference에 언어 기능을 문서화하는 것.
  • 새로운 구문을 포맷할 수 있도록 rustfmt를 확장하는 것(해당하는 경우).
  • rust-analyzer를 확장하는 것(해당하는 경우). 일부 기능은 완전한 지원이 갖춰질 때까지 막아 둘 필요가 없으므로, 이 작업의 범위는 언어 기능의 성격에 따라 달라질 수 있습니다.
    • rust-analyzer에서 지원이 구현되기 전에 언어 기능이 존재하는 것만으로 사용자 경험이 저하된다면, lang 팀이 이를 차단 우려 사항으로 제기할 수 있습니다.
    • 그러한 예로는 rust-analyzer가 파싱할 수 없는 새로운 구문이나, 이해하지 못하는 타입 추론 변경으로 인해 잘못된 진단이 발생하는 경우 등을 들 수 있습니다.

안정화

기능 생명 주기의 마지막 단계는 안정화이며, 이 단계에서 해당 기능을 모든 Rust 사용자가 사용할 수 있게 됩니다. 이 시점부터는 일반적으로 하위 호환성을 깨는 변경이 더 이상 허용되지 않습니다(자세한 내용은 lang 팀의 정의된 semver 정책을 참고하십시오). 안정화에 대해 더 알아보려면 안정화 가이드를 참고하십시오.