Skip to content

Функции

This content is not available in your language yet.

Большая часть того, что вы знаете о функциях из Python, работает и здесь. Но есть одно понятие, которого в Python нет вовсе, — и именно с него начинается настоящая сила Mojo.

def имя[параметры](аргументы) -> тип_возврата:
тело

Обязательно только имя и тело. Всё остальное — по необходимости, кроме типов аргументов: они нужны всегда.

def greet(name: String, greeting: String = "Привет") -> String:
return greeting + ", " + name + "!"

Функция без возвращаемого значения просто не имеет стрелки, либо пишется -> None — оба варианта равнозначны.

В Python «аргумент» и «параметр» — синонимы, и путать их не страшно. В Mojo это два разных механизма, и разница принципиальная:

АргументыПараметры
Скобкикруглые ( )квадратные [ ]
Когда известныво время выполненияна этапе компиляции
Что с ними делает компиляторпередаёт в функциюгенерирует отдельную версию функции
def repeat[count: Int](text: String):
for _ in range(count):
print(text, end=" ")
print()
def main():
repeat[3](String("эй"))
Результат

эй эй эй

Здесь count — параметр: компилятор знает, что повторов ровно три, и может развернуть цикл в три вызова print без всякого счётчика. text — обычный аргумент, его значение станет известно только при запуске.

Значения по умолчанию и именованные аргументы

Заголовок раздела «Значения по умолчанию и именованные аргументы»

Работают как в Python:

greet("Мир") # позиционно
greet(name="Mojo", greeting="Здравствуй") # по именам

Обязательные аргументы идут перед аргументами со значением по умолчанию. Можно сделать аргумент только именованным, поставив перед ним звёздочку:

def connect(host: String, *, timeout: Int = 30) -> String:
return host + ":" + String(timeout)
def main():
print(connect("example.com", timeout=5))
Результат

example.com:5

Такие аргументы нельзя передать позиционно — это защита от вызовов вида connect("example.com", 5, True, False), где через полгода никто не помнит, что означает каждое число.

def total(*values: Int) -> Int:
var sum = 0
for value in values:
sum += value
return sum
def main():
print(total(1, 2, 3, 4))
Результат

10

Обратите внимание: у вариативных аргументов тоже есть тип. *values: Int означает «сколько угодно целых», а не «сколько угодно чего угодно».

Одно имя может обслуживать разные типы — компилятор выберет нужную версию по типам аргументов:

def describe(value: Int):
print("целое:", value)
def describe(value: String):
print("строка:", value)
def main():
describe(42)
describe(String("сорок два"))
Результат

целое: 42 строка: сорок два

Это не «магия»: выбор происходит на этапе компиляции, накладных расходов при вызове нет.

error: redefinition of function 'show' with identical signature
Что это значит

Две функции с одним именем и одинаковыми типами аргументов. Компилятору нечем их различить.

Как исправить

Перегрузка различает функции по типам аргументов, а не по именам или типу возврата. Дайте разные имена либо разные типы.

Отдельного синтаксиса нет — используется кортеж:

def divide(a: Int, b: Int) -> Tuple[Int, Int]:
return (a // b, a % b)
def main():
var result = divide(17, 5)
print(result[0], result[1])
Результат

3 2

По умолчанию аргумент передаётся только для чтения: функция видит значение, но изменить его у вызывающего кода не может. Если менять нужно — пишется mut:

def bump(mut counter: Int):
counter += 1
def main():
var counter = 10
bump(counter)
print(counter)
Результат

11

Кроме mut есть ещё var (передача владения), ref и out — им посвящена отдельная глава, потому что за ними стоит вся модель памяти языка.

error: 'mut' arguments must not have defaults
Что это значит

У изменяемого аргумента нельзя задать значение по умолчанию.

Как исправить

Логично: менять нечего, если аргумент не передали. Уберите значение по умолчанию либо сделайте аргумент обычным.

Обе привычные возможности на месте:

def factorial(n: Int) -> Int:
if n <= 1:
return 1
return n * factorial(n - 1)
def main():
def double(x: Int) -> Int:
return x * 2
print(double(21), factorial(10))
Результат

42 3628800

Функции, которые могут бросить исключение

Заголовок раздела «Функции, которые могут бросить исключение»

Помечаются словом raises:

def parse(text: String) raises -> Int:
return Int(text)

Вызывающий код обязан это учесть: либо обернуть вызов в try, либо тоже объявить себя raises. В Python исключение может прилететь откуда угодно; в Mojo способность бросать исключение — часть контракта функции.

Приятная мелочь, которой нет в Python:

warning: doc string summary should begin with a capital letter
or non-alpha character, but this begins with 'm'

Mojo следит за оформлением документации к функциям — и подсказывает, если первая строка докстринга написана небрежно. Мелочь, а дисциплинирует.

signatures.mojo
def greet(name: String, greeting: String = "Привет") -> String:
"""Значение по умолчанию и именованные аргументы — как в Python."""
return greeting + ", " + name + "!"
def describe(value: Int):
"""Перегрузка: одно имя, разные типы аргумента."""
print("целое:", value)
def describe(value: String):
print("строка:", value)
def divide(a: Int, b: Int) -> Tuple[Int, Int]:
"""Вернуть несколько значений можно кортежем."""
return (a // b, a % b)
def total(*values: Int) -> Int:
"""Вариативные аргументы: сколько угодно чисел."""
var sum = 0
for value in values:
sum += value
return sum
def bump(mut counter: Int):
"""Аргумент mut означает, что функция меняет переменную вызывающего кода."""
counter += 1
def repeat[count: Int](text: String):
"""Параметр count известен на этапе компиляции — он в квадратных скобках."""
for _ in range(count):
print(text, end=" ")
print()
def main():
print(greet("Мир"))
print(greet(name="Mojo", greeting="Здравствуй"))
describe(42)
describe(String("сорок два"))
var result = divide(17, 5)
print("частное и остаток:", result[0], result[1])
print("сумма:", total(1, 2, 3, 4))
var counter = 10
bump(counter)
print("после bump:", counter)
repeat[3](String("эй"))
Результат

Привет, Мир! Здравствуй, Mojo! целое: 42 строка: сорок два частное и остаток: 3 2 сумма: 10 после bump: 11 эй эй эй

🎯 Проверь себя

Чем параметр в квадратных скобках отличается от аргумента в круглых?

Параметр известен на этапе компиляции, и компилятор генерирует под каждое его значение отдельную специализированную версию функции. Аргумент передаётся во время выполнения. Из этого различия и растёт производительность Mojo.

Можно ли перегрузить функцию, изменив только тип возвращаемого значения?

Нет. Перегрузка различает функции по типам аргументов. Две функции с одинаковыми аргументами дадут ошибку redefinition of function with identical signature, даже если возвращают разное.

Как вернуть из функции два значения?

Кортежем: -> Tuple[Int, Int] и return (a, b). Отдельного синтаксиса множественного возврата, как и в Python, нет — там тоже возвращается кортеж, просто скобки не пишут.

Операторы: арифметика, сравнения и то, как научить свою структуру складываться через +.

Примеры проверены на Mojo 1.0.0

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