주석

주석 (Comments)

코드를 읽는 사람과 소통하는 가장 쉬운 방법, 주석이에요. Rust의 주석은 크게 일반 주석(non-doc comment)문서 주석(doc comment) 으로 나뉘어요. 각각 언제 어떻게 쓰는지, 그리고 컴파일러가 어떻게 해석하는지 정리해 볼게요.

출처: Rust Reference

본문

문법 (Syntax)

COMMENT → LINE_COMMENT
        | INNER_LINE_DOC
        | OUTER_LINE_DOC
        | INNER_BLOCK_DOC
        | OUTER_BLOCK_DOC
        | BLOCK_COMMENT

LINE_COMMENT → // ( ~[/ ! [LF]] | // ) ~[LF]*
             | // [EOF]
             | // immediately followed by LF

BLOCK_COMMENT → /* ^ ( BLOCK_COMMENT_OR_DOC | ( !*/ [CHAR] ) )* */

INNER_LINE_DOC → //! ^ LINE_DOC_COMMENT_CONTENT ( [LF] | [EOF] )

LINE_DOC_COMMENT_CONTENT → ( ![CR] ~[LF] )*

INNER_BLOCK_DOC → /*! ^ ( BLOCK_COMMENT_OR_DOC | BLOCK_CHAR )* */

OUTER_LINE_DOC → /// ^ LINE_DOC_COMMENT_CONTENT ( [LF] | [EOF] )

OUTER_BLOCK_DOC → /** ![* /] ^
                     ( ~* | BLOCK_COMMENT_OR_DOC )
                     ( BLOCK_COMMENT_OR_DOC | BLOCK_CHAR )* */

BLOCK_CHAR → ( !( */ | [CR] ) [CHAR] )

BLOCK_COMMENT_OR_DOC → INNER_BLOCK_DOC | OUTER_BLOCK_DOC | BLOCK_COMMENT

일반 주석 (Non-doc comments)

일반 주석은 C++ 스타일의 한 줄 주석(//)블록 주석(/* ... */) 형태를 따르고, 블록 주석의 중첩(nested block comments) 을 지원해요.

#![allow(unused)]
fn main() {
// 한 줄 주석
/* 블록 주석 */
/* 중첩도 /* 가능해요 */ */
}

일반 주석은 공백(whitespace)의 한 형태로 해석돼요. 즉 토큰을 구분하는 역할만 하고, 컴파일 결과에 아무 영향을 주지 않아요.

문서 주석 (Doc comments)

바깥 문서 주석(outer doc comments) 은 정확히 슬래시 세 개(///)로 시작하는 한 줄 문서 주석과 블록 문서 주석(/** ... */)이에요. 이것들은 doc 속성을 위한 특별한 문법으로 해석돼요.

즉, 주석 본문을 #[doc="..."] 로 감싼 것과 동일해요. 예를 들어 /// Foo#[doc=" Foo"] 가 되고, /** Bar */#[doc=" Bar "] 가 돼요. 따라서 바깥 속성(outer attribute)을 받을 수 있는 무언가 앞에 나타나야 해요.

반면 //! 로 시작하는 한 줄 주석과 /*! ... */ 블록 주석은 안쪽 문서 주석(inner doc comments) 으로, 뒤에 오는 아이템이 아니라 주석의 부모에 적용돼요. 이건 #![doc="..."] 로 감싼 것과 동일하죠. //! 주석은 보통 하나의 소스 파일을 차지하는 모듈을 문서화할 때 써요.

문서 주석에서는 U+000D(CR) 문자를 사용할 수 없어요.

참고 1: 관례상 문서 주석은 rustdoc 이 기대하듯 Markdown을 담아요. 하지만 주석 문법 자체는 내부의 Markdown을 해석하지 않아요. 예를 들어 /** glob = "/.rs"; */ 처럼 쓰면 첫 번째 */ 에서 주석이 끝나 버려서, 남은 코드가 문법 오류가 돼요. 그래서 블록 문서 주석은 한 줄 문서 주석보다 담을 수 있는 내용이 조금 제약돼요.

참고 2: U+000D(CR) 바로 뒤에 U+000A(LF)가 오는 시퀀스는 이전에 하나의 U+000A(LF)로 변환되곤 했어요.

예시 (Examples)

다음 예시로 주석 종류를 한눈에 확인할 수 있어요.

#![allow(unused)]
fn main() {
//! A doc comment that applies to the implicit anonymous module of this crate

pub mod outer_module {

    //!  - Inner line doc
    //!! - Still an inner line doc (but with a bang at the beginning)

    /*!  - Inner block doc */
    /*!! - Still an inner block doc (but with a bang at the beginning) */

    //   - Only a comment
    ///  - Outer line doc (exactly 3 slashes)
    //// - Only a comment

    /*   - Only a comment */
    /**  - Outer block doc (exactly) 2 asterisks */
    /*** - Only a comment */

    pub mod inner_module {}

    pub mod nested_comments {
        /* In Rust /* we can /* nest comments */ */ */

        // All three types of block comments can contain or be nested inside
        // any other type:

        /*   /* */  /** */  /*! */  */
        /*!  /* */  /** */  /*! */  */
        /**  /* */  /** */  /*! */  */
        pub mod dummy_item {}
    }

    pub mod degenerate_cases {
        // empty inner line doc
        //!

        // empty inner block doc
        /*!*/

        // empty line comment
        //

        // empty outer line doc
        ///

        // empty block comment
        /**/

        pub mod dummy_item {}

        // empty 2-asterisk block isn't a doc block, it is a block comment
        /***/

    }

    /* The next one isn't allowed because outer doc comments
       require an item that will receive the doc */

    /// Where is my item?
  mod boo {}
}
}

여기서 핵심만 짚어 볼게요.

  • 슬래시가 정확히 3개 (///)여야 바깥 문서 주석, 4개(////)면 그냥 주석이에요.
  • 별표가 정확히 2개 (/**)여야 바깥 블록 문서 주석, 3개(/***)면 그냥 주석이에요.
  • /*!//! 는 안쪽 문서 주석이라, 모듈처럼 부모 항목에 붙어요.
  • 블록 주석은 서로 다른 종류끼리도 자유롭게 중첩될 수 있어요.
  • 바깥 문서 주석(///)은 문서를 받을 아이템이 반드시 뒤에 필요해요. 예시 마지막에 있는 /// Where is my item? 처럼 아이템 없이 쓰면 안 돼요.

더 알아보기 (Learn more)