Core Constructs

Comments

Single-line comments:

// This is a single-line comment
pub struct User { name: String }  // Inline comment

Multi-line comments:

/*
 * This is a multi-line comment
 * spanning several lines
 */
pub struct Post { title: String }

Doc comments use /// (item-level) or //! (parent / file-level). A /// comment attaches to the declaration after it. They flow through to the IR and are available to backends as the doc: field on most definitions.

A //! comment documents the file or the mod that holds it. Put it at the start of the file, or at the start of the body of a mod. The AST keeps it in File.doc or in the doc field of the module. At any other place, a //! comment is a ParseError:

//! Shapes and their areas.

/// A square.
pub struct Square { side: I32 }

pub mod units {
  //! The units of a length.
  pub struct Metre { value: I32 }
}

Bidirectional control characters: a comment or a string must not hold the characters U+202A to U+202E and U+2066 to U+2069. These characters change the direction of the text around them, so an editor can show the source in an order that is not the order that the compiler reads (CVE-2021-42574, "Trojan Source"). The compiler refuses them with the error BidirectionalControl. In a string, write the escape \u202E if you need such a character. The escape is visible in the source, so the compiler accepts it:

pub let marker: String = "\u202E"

pub fn run_checks() {
  assert(condition: marker.len() == 3)   // three bytes of UTF-8
}

Statements

In a function body or a block, a statement ends at a line break, at the } that closes the block, or at a { that starts a block statement. There is no terminator, and two statements on one line are an error:

pub fn f() -> I32 {
    let a = 1 let b = 2
    a + b
}

A line that ends with an operator, a , or an open bracket continues on the next line. A line that starts with ., else or { continues the line above:

pub fn pick(big: Boolean) -> I32 {
  let base = 10 +
    5
  let total = [1, 2, 3]
    .len()
  if big
  {
    base
  }
  else
  {
    total
  }
}

A line that starts with another operator, such as - or +, is a new statement.

The rule also applies at module level, in the body of a mod and in the body of an impl. Each definition starts on a new line:

pub let a: I32 = 1 pub let b: I32 = 2

Nesting Depth

The compiler has a limit on how deep a program nests. Each open (, [ or { adds one level, and so does each operator in a chain such as 1 + 1 + 1. Above 1024 levels, the parser stops with a ParseError. An expression more than 500 levels deep gets the error ExpressionDepthExceeded (E130). A real program is far below both limits. If you reach one, put a part of the expression in a let.

Visibility Modifiers

Control export visibility with the pub keyword:

// Public - can be imported by other modules
pub struct User { name: String }
pub trait Named { name: String }
pub enum Status { active, inactive }
pub let MAX_USERS: I32 = 100

// Private - module-local only (default)
struct Internal { id: I32 }
trait Helper { key: String }
enum State { ready, processing }
let secret_key: String = "xyz"

Keywords

Reserved words that cannot be used as identifiers. self is one of them: it names the receiver of a method, and nothing else. See Traits & Impls.

trait    struct   enum     use      pub      impl     mod
let      mut      sink     match    for      in       if
else     true     false    nil      as       extern
fn       self     inline   no_inline cold