Skip to content

Шпаргалка по синтаксису

This content is not available in your language yet.

Почти каждый блок ниже — законченная программа: её можно скопировать в файл и запустить через uv run mojo файл.mojo. Под программой — то, что она печатает. И код, и вывод проверяются компилятором при каждом изменении курса, так что шпаргалка не отстанет от языка.

Подробности — в главах, ссылки на них стоят в конце каждого раздела.

# Комментарий — как в Python
comptime GREETING = "Привет" # константа этапа компиляции
def main():
print(GREETING, "Mojo") # аргументы через пробел
print("a", "b", "c", sep="-") # свой разделитель
print("без перевода строки", end="")
print(" — продолжение")
Результат
Привет Mojo
a-b-c
без перевода строки — продолжение

Код выполняется только внутри функций: точка входа — def main(). Подробнее: Первая программа.

def main() raises:
var count = 10 # тип выводится: Int
var price: Float64 = 99.5 # тип указан явно
var name: String = "Mojo"
var ready = True # Bool
count += 1 # дальше var не нужен
var small: Int8 = 127 # размерные: Int8…Int64, UInt8…UInt64
var ratio: Float32 = 0.5 # Float16, Float32, Float64
# Преобразования — только явные
print(Float64(count) * price) # Int → Float64
print(Int(price)) # Float64 → Int: дробь отбрасывается
print(String(42) + "!") # число → строка
print(Int("123") + 1) # строка → число, может бросить ошибку
var a = 7
var b = 2
print(a / b, a // b, a % b, a**2) # Int / Int даёт Int!
print(Float64(a) / Float64(b))
print(name, ready, small, ratio)
Результат
1094.5
99
42!
124
3 3 1 49
3.5
Mojo True 127 0.5

Новое имя объявляется через var: просто x = 5 без объявления — ошибка компиляции. Подробнее: Переменные, Система типов.

def main():
var s = String("Привет, мир")
print(s.byte_length(), len(s.codepoints())) # байты и символы
print(s[codepoint=0:6]) # срез по символам
print(s[byte=0:12]) # срез по байтам
print(s.upper(), s.find("мир"), "мир" in s) # find — позиция в байтах
print(s.replace("мир", "Mojo"), s.startswith("Прив"))
var parts = String("a,b,c").split(",")
print(len(parts), parts[0])
print(String("-").join(parts))
var x = 42
print(t"x = {x}, x² = {x * x}") # t-строка вместо f-строки
var line = String(t"x = {x}") # t-строка -> String
print(line, String("y = ", 1.5)) # String(...) склеивает значения
for ch in String("ёж").codepoints():
print(ch, end=" ")
print()
Результат
20 11
Привет
Привет
ПРИВЕТ, МИР 14 True
Привет, Mojo True
3 a
a-b-c
x = 42, x² = 1764
x = 42 y = 1.5
ё ж 

len(строки) не компилируется: скажите явно, что считать — байты (byte_length()), символы (len(s.codepoints())) или видимые знаки (len(s.graphemes())). Вместо f-строк — t-строки t"x = {x}": спецификаторов формата вроде {x:.2f} у них нет, округляйте в скобках — t"{round(x, 2)}". Подробнее: Строки и Unicode.

from std.collections import Set # Set импортируется явно
# raises: ages["ключ"] бросает ошибку, если ключа нет
def main() raises:
var nums: List[Int] = [3, 1, 2] # без аннотации будет Array, а не List
nums.append(5)
print(len(nums), nums[0], nums[len(nums) - 1])
var squares: List[Int] = [x * x for x in range(4)]
print(squares)
var ages = Dict[String, Int]()
ages["Аня"] = 30
ages["Борис"] = 25
print("Аня" in ages, ages["Аня"], ages.get("Вера", 0))
for item in ages.items():
print(item.key, item.value)
var tags = Set[String]("mojo", "python")
tags.add("mojo")
print(len(tags), "mojo" in tags)
var point = (3, "три") # кортеж
var n, word = point # распаковка
print(point[0], n, word)
var maybe = Optional[Int](42) # значение или ничего
var nothing = Optional[Int]()
if maybe:
print(maybe.value(), Bool(nothing))
Результат
4 3 5
[0, 1, 4, 9]
True 30 0
Аня 30
Борис 25
2 True
3 3 три
42 False

Элементы коллекции — одного типа. Аннотация у списка важна: без неё var nums = [3, 1, 2] — это Array[Int, 3] фиксированной длины, и append у него нет. Отрицательных индексов нет — последний элемент nums[len(nums) - 1]. List, Dict и Optional доступны без импорта, Set — из std.collections. Сортировка — функция sort(xs), а не метод. Подробнее: Коллекции и итераторы.

def main():
var t = 25
if t > 30:
print("жарко")
elif t > 20:
print("тепло")
else:
print("прохладно")
print("шорты" if t > 20 else "брюки") # тернарный оператор
for i in range(0, 10, 3):
print(i, end=" ")
print()
var cities: List[String] = ["Москва", "Казань"]
for i, city in enumerate(cities):
print(i, city)
var numbers: List[Int] = [1, 2, 3]
for ref x in numbers: # ref — меняем элементы
x *= 10
print(numbers)
var n = 0
while n < 5:
n += 1
if n == 2:
continue
if n == 4:
break
else:
print("не выполнится: был break")
print("n =", n)
Результат
тепло
шорты
0 3 6 9 
0 Москва
1 Казань
[10, 20, 30]
n = 4

else у цикла срабатывает, только если не было break. Подробнее: Управляющие конструкции.

def greet(name: String, greeting: String = "Привет") -> String:
return greeting + ", " + name + "!"
def divmod2(a: Int, b: Int) -> Tuple[Int, Int]:
return (a // b, a % b)
def total(*values: Int) -> Int: # сколько угодно аргументов
var sum = 0
for v in values:
sum += v
return sum
def clamp(x: Int, *, low: Int, high: Int) -> Int: # после * — только по имени
return max(low, min(x, high))
def describe(x: Int): # перегрузка по типу
print("целое", x)
def describe(x: String):
print("строка", x)
def repeat[times: Int](text: String): # параметр этапа компиляции
for _ in range(times):
print(text, end=" ")
print()
def main():
print(greet("Мир"), greet("Mojo", greeting="Здравствуй"))
var q, r = divmod2(17, 5)
print(q, r, total(1, 2, 3), clamp(15, low=0, high=10))
describe(1)
describe("один")
repeat[3]("эй")
var square = lambda (x: Int) -> Int: x * x
print(square(7))
Результат
Привет, Мир! Здравствуй, Mojo!
3 2 6 10
целое 1
строка один
эй эй эй 
49

Типы аргументов обязательны. Параметры в квадратных скобках известны при сборке, аргументы в круглых — при запуске. Подробнее: Функции, Параметризация.

def parse_age(text: String) raises -> Int: # raises: может бросить ошибку
var value = Int(text)
if value < 0:
raise Error("отрицательный возраст")
return value
def main():
for text in ["30", "-5", "abc"]:
try:
print("возраст:", parse_age(text))
except e:
print("ошибка:", e)
else:
print(" ошибок не было")
finally:
print(" finally — всегда")
Результат
возраст: 30
  ошибок не было
  finally — всегда
ошибка: отрицательный возраст
  finally — всегда
ошибка: String is not convertible to integer with base 10: 'abc'
  finally — всегда

Функция, которая может бросить ошибку, помечается raises — и вызвать её можно только из raises-функции или внутри try с except. Подробнее: Обработка ошибок.

@fieldwise_init # конструктор из полей по порядку
struct Point(ImplicitlyCopyable, Writable):
var x: Int
var y: Int
comptime ORIGIN_NAME = "начало координат" # константа типа
@staticmethod
def origin() -> Point:
return Point(0, 0)
def length2(self) -> Int: # self только читается
return self.x * self.x + self.y * self.y
def shift(mut self, dx: Int): # mut self — метод меняет объект
self.x += dx
def __add__(self, other: Self) -> Self: # оператор +
return Point(self.x + other.x, self.y + other.y)
def __eq__(self, other: Self) -> Bool: # оператор ==
return self.x == other.x and self.y == other.y
def write_to[W: Writer](self, mut writer: W): # для print и String()
writer.write("(", self.x, ", ", self.y, ")")
struct Counter:
var count: Int
def __init__(out self, start: Int = 0): # свой конструктор
self.count = start
def main():
var p = Point(3, 4)
p.shift(1)
print(p, p.length2(), p + Point(1, 1), p == Point(4, 4))
print(Point.origin(), Point.ORIGIN_NAME, Counter(5).count)
Результат
(4, 4) 32 (5, 5) True
(0, 0) начало координат 5

Классов нет, есть struct: набор полей фиксирован при компиляции. write_to делает значение печатаемым и через print, и через String(). Подробнее: Структуры, Операторы.

def show(items: List[Int]): # по умолчанию: только чтение
print(items)
def append_one(mut items: List[Int]): # mut: меняем значение вызывающего
items.append(1)
def consume(var items: List[Int]): # var: функция владеет значением
items.append(99)
print("внутри:", items)
def make_list(out result: List[Int]): # out: результат через аргумент
result = [7, 8]
def longer(ref a: String, ref b: String) -> ref[a, b] String: # ссылка
return a if a.byte_length() > b.byte_length() else b
def main():
var a: List[Int] = [1, 2]
show(a)
append_one(a)
var b = a.copy() # явная копия: List не копируется молча
consume(a^) # ^ передаёт владение; a больше нет
print(b, make_list())
var s1 = String("раз")
var s2 = String("двадцать")
longer(s1, s2) = "заменено" # запись по возвращённой ссылке
print(s1, s2)
Результат
[1, 2]
внутри: [1, 2, 1, 99]
[1, 2, 1] [7, 8]
раз заменено
КонвенцияЧто может функцияКак вызывать
(без пометки)только читатьf(x)
mutменять значение вызывающегоf(x)
varвладеть значениемf(x^) — отдать, f(x.copy()) — копию; простые типы копируются сами: f(x)
outсоздать значение-результатvar r = f()
refполучить и вернуть ссылкуf(x)

var b = a для списка не компилируется: List не копируется молча. Пишите a.copy() или a^. После a^ переменная a недоступна. Подробнее: Конвенции передачи аргументов, Копирование и перемещение.

trait Shape:
def area(self) -> Float64: # обязательный метод
...
def name(self) -> String: # метод с реализацией по умолчанию
return "фигура"
@fieldwise_init
struct Square(Copyable, Shape): # структура объявляет трейты
var side: Float64
def area(self) -> Float64:
return self.side * self.side
@fieldwise_init
struct Circle(Copyable, Shape):
var r: Float64
def area(self) -> Float64:
return 3.14159 * self.r * self.r
def name(self) -> String:
return "круг"
def report[T: Shape](s: T): # обобщённая функция
print(s.name(), s.area())
def total_area[T: Shape & Copyable](items: List[T]) -> Float64: # два трейта
var sum = 0.0
for item in items:
sum += item.area()
return sum
def main():
report(Square(2))
report(Circle(1))
var squares: List[Square] = [Square(1), Square(2)]
print(total_area(squares))
Результат
фигура 4.0
круг 3.14159
5.0

Трейт — набор требований. Функция report[T: Shape] работает с любым типом, который его выполняет, и компилируется отдельно под каждый. Подробнее: Трейты и обобщённый код.

comptime SIZE = 4 # константа этапа компиляции
comptime Doubled[n: Int]: Int = n * 2 # параметризованная константа
def fib(n: Int) -> Int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
struct Vec[n: Int]: # параметр типа
var data: Array[Float64, Self.n]
def __init__(out self, fill: Float64):
self.data = Array[Float64, Self.n](fill=fill)
def sum(self) -> Float64:
var s = 0.0
comptime for i in range(Self.n): # цикл разворачивается при сборке
s += self.data[i]
return s
def positive[n: Int]() -> Int where n > 0: # ограничение на параметр
return n
def main():
comptime F20 = fib(20) # посчитано при сборке
comptime assert SIZE % 2 == 0, "SIZE должен быть чётным"
comptime if SIZE > 2: # ветка выбирается при сборке
print("большой размер")
else:
print("маленький размер")
print(F20, Doubled[21], Vec[SIZE](1.5).sum(), positive[3]())
Результат
большой размер
6765 42 6.0 3

Всё, что помечено comptime, считается при сборке — в программе остаются готовые значения. Подробнее: Вычисления на этапе компиляции, Ограничения.

def main():
var v = SIMD[DType.float32, 4](1, 2, 3, 4) # четыре числа в регистре
print(v * 2, v + v, v.reduce_add(), v[0])
var mask = v.gt(2) # сравнение по элементам
print(mask, mask.select(v, 0)) # выбор по маске
var data: List[Float32] = [1, 2, 3, 4, 5, 6, 7, 8]
var ptr = data.unsafe_ptr() # указатель на элементы
var chunk = ptr.unsafe_load[width=4](4) # загрузить 4 числа с индекса 4
ptr.unsafe_store(0, chunk * 10) # записать 4 числа в начало
print(data)
Результат
[2.0, 4.0, 6.0, 8.0] [2.0, 4.0, 6.0, 8.0] 10.0 1.0
[False, False, True, True] [0.0, 0.0, 3.0, 4.0]
[50.0, 60.0, 70.0, 80.0, 5.0, 6.0, 7.0, 8.0]

Сравнение векторов поэлементно — через методы gt, lt, eq и другие. Оператор > для вектора не компилируется, а == компилируется, но отвечает одним Bool за весь вектор. unsafe_load и unsafe_store границ не проверяют. Подробнее: SIMD с нуля, Указатели и память.

from std.algorithm import vectorize
from max.algorithm import parallelize # нужен пакет max: uv add max
def main():
var data = List[Float32](length=10, fill=1)
var ptr = data.unsafe_ptr()
# vectorize: нарезает массив на векторы ширины 4 и досчитывает хвост
def double[width: Int](i: Int) {imm ptr}:
ptr.unsafe_store(i, ptr.unsafe_load[width=width](i) * 2)
vectorize[4](len(data), double)
print(data)
# parallelize: вызывает функцию для 0, 1, …, n-1 на разных ядрах
var squares = List[Int](length=8, fill=0)
var dst = squares.unsafe_ptr()
def work(i: Int) {imm dst}:
dst[unsafe_offset=i] = i * i
parallelize(work, 8)
print(squares)
Результат
[2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
[0, 1, 4, 9, 16, 25, 36, 49]

Список захвата в фигурных скобках — {imm ptr} — говорит, какие внешние переменные и как захватывает вложенная def. Без него она не соберётся. Подробнее: Векторизация и параллелизм, Замыкания и лямбды.

Общую переменную из нескольких потоков менять нельзя: компилятор это пропустит, а ответ будет неверным. Счётчик — Atomic[Int] из std.atomic, общий список — под BlockingSpinLock из std.utils, а быстрее всего — своя ячейка на каждый поток. GIL нет, async в 1.1 нестабилен. Подробнее: Потоки, процессы и async.

from std.python import Python
def main() raises:
var math = Python.import_module("math") # любой модуль Python
var root = math.sqrt(16) # результат — PythonObject
print(root, Float64(py=root) + 1) # явное преобразование
var json = Python.import_module("json")
var data = json.loads('{"name": "Mojo", "tags": ["fast", "typed"]}')
var name = String(py=data["name"])
print(name, len(data["tags"]))
for tag in data["tags"]:
print(tag)
Результат
4.0 5.0
Mojo 2
fast
typed

Всё, что пришло из Python, — PythonObject. В родные типы Mojo его переводят явно: Int(py=...), Float64(py=...), String(py=...). Подробнее: Вызов Python из Mojo.

from std.pathlib import Path
from std.random import random_si64, seed
from std.tempfile import TemporaryDirectory
def main() raises:
with TemporaryDirectory() as tmp:
var p = Path(tmp) / "log.txt"
with open(p, "w") as f: # режимы: "r", "w", "a", "rw"
f.write("строка ", 1, "\n") # без пробелов и без \n в конце
with open(p, "a") as f:
f.write("строка 2\n")
for line in p.read_text().splitlines(): # построчно — только так
print(line)
seed(2026) # без seed числа одинаковы при каждом запуске
print(Int(random_si64(1, 6))) # как random.randint(1, 6)
Результат
строка 1
строка 2
4

Модули стандартной библиотеки — только с префиксом std.. Для замеров — perf_counter_ns() из std.time, для текущей даты — питоновский datetime. Подробнее: Стандартная библиотека на каждый день.

import geometry # geometry.mojo рядом
from geometry import to_centimeters # отдельное имя
from shapes import Circle, Rectangle # пакет: папка с __init__.mojo
from std.math import sqrt # стандартная библиотека

Подробнее: Модули и пакеты.

from std.testing import assert_equal, assert_raises, assert_true, TestSuite
def parse(text: String) raises -> Int:
return Int(text)
def test_parse() raises: # тест — функция с именем test_*
assert_equal(parse("42"), 42)
assert_true(parse("7") > 0)
def test_bad_input() raises:
with assert_raises(): # ждём ошибку
_ = parse("abc")
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()

Отдельной команды для тестов нет: файл запускается как обычная программа, TestSuite сам находит функции test_*. Подробнее: Тестирование и отладка.

Окно терминала
uv run mojo app.mojo # собрать и запустить
uv run mojo build app.mojo -o app # исполняемый файл
uv run mojo run -D ASSERT=all app.mojo # включить все debug_assert
uv run mojo build app.mojo -D SIZE=64 # значение для get_defined_int
uv run mojo format . # отформатировать код
uv run mojo precompile shapes -o shapes.mojoc # предкомпилировать пакет
uv run mojo debug app.mojo # отладчик
uv run mojo --version
Примеры проверены на Mojo 1.1.0

Тексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0