Шпаргалка по синтаксису
Почти каждый блок ниже — законченная программа: её можно скопировать
в файл и запустить через uv run mojo файл.mojo. Под программой — то,
что она печатает. И код, и вывод проверяются компилятором при каждом
изменении курса, так что шпаргалка не отстанет от языка.
Подробности — в главах, ссылки на них стоят в конце каждого раздела.
Программа и вывод
Заголовок раздела «Программа и вывод»# Комментарий — как в Pythoncomptime 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_initstruct Square(Copyable, Shape): # структура объявляет трейты var side: Float64
def area(self) -> Float64: return self.side * self.side
@fieldwise_initstruct 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, считается при сборке — в программе остаются
готовые значения. Подробнее: Вычисления на этапе компиляции,
Ограничения.
SIMD и указатели
Заголовок раздела «SIMD и указатели»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 vectorizefrom 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.
Python из Mojo
Заголовок раздела «Python из Mojo»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 Pathfrom std.random import random_si64, seedfrom 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__.mojofrom 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_assertuv run mojo build app.mojo -D SIZE=64 # значение для get_defined_intuv run mojo format . # отформатировать кодuv run mojo precompile shapes -o shapes.mojoc # предкомпилировать пакетuv run mojo debug app.mojo # отладчикuv run mojo --versionТексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0