코딩 규칙
이 장에서는 포맷팅, 정확성을 위한 코딩, crates.io의 크레이트 사용을 다루고, 리뷰하기 쉽도록 PR을 구성하는 방법에 대한 몇 가지 팁을 소개합니다.
포맷팅과 tidy 스크립트
rustc는 Rust 표준 코딩 스타일을 따르는 방향으로 나아가고 있습니다.
하지만 현재로서는 안정 버전의 rustfmt를 사용하지 않고 특별한 설정을 적용한 고정 버전을 사용하므로, 일반적인 rustfmt와는 다른 스타일이 나올 수 있습니다. 따라서 cargo fmt로 이 저장소를 포맷팅하는 것은 권장하지 않습니다.
대신 포맷팅은 ./x fmt로 해야 합니다. 커밋하기 전마다 ./x fmt를 실행하는 습관을 들이는 것이 좋습니다. 이렇게 하면 나중에 충돌이 줄어듭니다.
포맷팅은 tidy 스크립트가 검사합니다. 이 스크립트는 ./x test를 실행할 때 자동으로 실행되며, ./x fmt --check로 따로 실행할 수도 있습니다.
참고: 포맷팅과 테스트 스위트
tests/디렉터리 아래의 Rust 소스 파일 대부분은 공백 민감성, 스냅숏 테스트의 특성, 위치에 민감한 주석 등의 이유로 포맷팅되지 않습니다.어떤 테스트 파일이 포맷팅되지 않는지는 https://github.com/rust-lang/rust/blob/main/rustfmt.toml의
ignore항목을 참고하십시오.
편집기에서 저장 시 포맷팅 기능을 사용하려면, build/<target>/stage0/bin/rustfmt에 빌드되는 고정 버전의 rustfmt를 사용하십시오.
C++ 코드 포맷팅
컴파일러에는 안정적인 C API가 없는 LLVM 일부와 연동하기 위한 C++ 코드가 일부 포함되어 있습니다. 해당 코드를 수정할 때는 다음 명령으로 포맷팅하십시오:
./x test tidy --extra-checks cpp:fmt --bless
이 명령은 로컬 환경에 의존하지 않도록 고정 버전의 clang-format을 사용합니다.
Python 코드 포맷팅과 린트
Rust 저장소에는 상당히 많은 Python 코드가 포함되어 있습니다. 저희는 ruff 도구로 이 코드의 린트와 포맷팅을 모두 관리하려고 노력합니다.
Python 코드를 수정할 때는 다음 명령으로 포맷팅하십시오:
./x test tidy --extra-checks py:fmt --bless
그리고 린트를 실행하려면 다음 명령을 사용하십시오:
./x test tidy --extra-checks py:lint
이 명령들은 로컬 환경에 의존하지 않도록 고정 버전의 ruff를 사용합니다.
저작권 고지
예전에는 파일 첫머리에 저작권 및 라이선스 고지가 있었습니다. 표준 조건(MIT OR Apache-2.0)으로 라이선스되는 새 파일에서는 이 고지를 생략하십시오.
지금쯤이면 저작권 고지가 모두 제거되었어야 하지만, rust-lang/rust 저장소에서 남아 있는 고지를 발견하면 언제든 이를 제거하는 PR을 여십시오.
줄 길이
한 줄은 최대 100자여야 합니다. 80자 이내로 유지할 수 있다면 더욱 좋습니다.
때로는, 특히 테스트의 경우, 이 제한에서 예외를 두어야 할 때가 있습니다. 그런 경우에는 다음과 같이 파일 상단 부근에 주석을 추가할 수 있습니다:
#![allow(unused)]
fn main() {
// ignore-tidy-linelength
}
탭 대 공백
4칸 들여쓰기를 사용하십시오.
정확성을 위한 코딩
서식 외에도 따를 만한 몇 가지 다른 팁이 있습니다.
모든 경우를 다루는 매치를 사용하십시오
매치에서 _를 사용하면 편리하지만, 열거형에 새로운 배리언트가 추가될 때 해당 배리언트가 올바르게 처리되지 않을 수 있습니다. 스스로에게 물어보십시오. 이 열거형에 새로운 배리언트가 추가된다면, 그 배리언트가 다른 방식으로 처리되어야 하는 것이 아니라 _ 코드를 사용해야 할 가능성은 얼마나 됩니까? 답이 “낮음“이 아니라면 모든 경우를 다루는 매치를 사용하십시오.
if let과 while let에도 같은 조언이 적용됩니다. 이 둘은 사실상 단일 배리언트에 대한 테스트이기 때문입니다.
잊고 싶지 않은 것에는 “TODO” 주석을 사용하십시오
PR을 병합하기 전에 다시 살펴보고 싶은 부분이 있다면, 스스로를 위한 유용한 도구로서 // TODO 주석을 삽입할 수 있습니다:
fn do_something() {
if something_else {
unimplemented!(); // TODO write this
}
}
tidy 스크립트는 // TODO 주석에 대해 오류를 보고하므로, 이 코드는 TODO가 수정(또는 제거)될 때까지 병합될 수 없습니다.
이는 PR 안에서 어떤 커밋이 이후 커밋에서 수정할 버그를 남겨 두고 있음을 알리는 방법으로도 유용할 수 있습니다:
if foo {
return true; // TODO wrong, but will be fixed in a later commit
}
코드베이스에 메모를 남기고 싶다면 대신 // FIXME를 사용하십시오.
주변 코드의 스타일을 따르십시오
기존 헬퍼를 사용하고 로직이나 검증을 중복하지 마십시오.
진실 공급원을 중복하지 마십시오
서로 다른 두 곳의 데이터를 동기화된 상태로 유지하려는 것은 코드 스멜입니다.
타입을 사용하여 불변 조건을 강제하십시오
가능하다면 생성 시점에 검사하는 데 그치지 말고 잘못된 상태를 아예 표현할 수 없게 만드십시오.
유용한 주석을 작성하십시오
무엇을 했는지가 아니라 왜 그렇게 했는지를 설명하는 주석을 작성하십시오. 명확하지 않은 버그에 대해서는 자세히 설명해도 괜찮습니다.
기존 동작을 유지하십시오
플랫폼 간 차이와 오류 상황을 고려하십시오. 엣지 케이스를 검증하는 관련 테스트를 찾아보십시오.
작은 단계로 나누어 작업하십시오
작고 독립적으로 테스트할 수 있는 단계로 나누어 작업하십시오. 의미 있는 변경을 할 때마다 관련 테스트를 실행하십시오. 그래야 처음 잘못된 지점이 어디인지 알 수 있습니다.
crates.io의 크레이트 사용하기
crates.io 의존성 절을 참고하십시오.
PR 구성 방법
PR의 커밋을 어떻게 준비하느냐에 따라 리뷰어의 부담이 크게 달라질 수 있습니다. 몇 가지 팁을 소개합니다.
“순수 리팩터링“은 별도의 커밋으로 분리하십시오. 예를 들어 메서드 이름을 바꾸는 경우, 그 이름 변경을 모든 사용처의 이름 변경과 함께 별도의 커밋에 넣으십시오.
일반적으로 커밋은 많을수록 좋습니다. 큰 변경을 하는 경우, 이를 독립적으로 이해할 수 있는 더 작은 단계로 나누는 것이 거의 항상 더 좋습니다. 한 가지 주의할 점은, 어떤 전략에 따라 코드를 도입한 다음 이후 커밋에서 그 코드를 (덧붙이는 것이 아니라) 크게 바꾸면 그러한 ’오락가락’이 혼란을 줄 수 있다는 것입니다.
포맷팅을 자주 하십시오. PR의 마지막 커밋만 올바르게 포맷팅되어 있으면 되지만, ./x fmt를 사용하여 각 커밋을 개별적으로 포맷팅하면 리뷰하기도 쉽고 불필요한 변경도 줄어듭니다.
병합하지 마십시오. bors가 만든 것 외에는 히스토리에 병합 커밋을 허용하지 않습니다. 병합 충돌이 발생하면 병합 대신 git rebase --interactive rust-lang/main과 같은 명령으로 리베이스하십시오(원격 저장소 이름으로 rust-lang을 사용한다고 가정합니다).
개별 커밋이 빌드될 필요는 없습니다(하지만 빌드되면 좋습니다). 모든 중간 커밋이 성공적으로 빌드되어야 한다고 요구하지는 않으며, PR 단위로 bisect할 수 있기만 하면 됩니다. 하지만 개별 커밋이 빌드되도록 할 수 있다면 언제나 도움이 됩니다.
명명 규칙
일반적인 Rust 스타일 및 명명 규칙 외에도, 컴파일러에만 해당하는 규칙이 몇 가지 있습니다.
-
cx는 대개 “context“의 약어이며, 접미사로 자주 사용됩니다. 예를 들어tcx는 타이핑 컨텍스트(Typing Context)를 가리키는 일반적인 이름입니다. -
'tcx는 타이핑 컨텍스트의 라이프타임 이름으로 사용됩니다. -
crate는 키워드이므로, 크레이트와 관련된 무언가를 나타내는 변수가 필요하면 철자를krate로 바꾸는 경우가 많습니다.