Представьте модуль, где тип и функция имеют одинаковое имя: как Rust определяет, какое имя искать в конкретном месте?
Rust использует раздельные пространства имён. Имя в позиции типа ищется в пространстве типов, а имя в позиции выражения — в пространстве значений, поэтому тип и функция могут иметь одинаковое написание без конфликта.
Это не означает, что имя универсально доступно в любом контексте: способ использования имени определяет, какое пространство имён проверяется.
Раздельные пространства имён позволяют отделить описание типов от исполняемых сущностей. Благодаря этому одинаковые имена могут естественно использоваться для разных ролей, не заставляя разработчика искусственно придумывать несвязанные названия.
Такой подход также поддерживает типобезопасное разрешение имён: компилятор учитывает синтаксическую позицию имени, а не пытается трактовать все сущности как элементы одного общего списка.
Без этого правила объявление типа и функции с одинаковым именем считалось бы неоднозначным даже там, где роль имени очевидна. Это усложнило бы API и сделало бы невозможными некоторые привычные соглашения именования.
Однако смешение контекстов приводит к ошибкам: имя, корректное как тип, нельзя автоматически использовать как функцию, значение или макрос. При анализе ошибки важно сначала определить, в какой позиции находится имя.
В Rust как минимум различаются пространство типов, пространство значений и пространство макросов. Псевдонимы типов, структуры, перечисления и параметры типов относятся к пространству типов; функции, переменные, константы и значения — к пространству значений.
В объявлении параметра или аннотации типа компилятор ищет имя среди типов. В вызове функции или другом выражении он ищет имя среди значений. Поэтому одинаковые имена могут сосуществовать, если относятся к разным пространствам.
В parser: Parser имя Parser трактуется как тип. В let parser: Parser = Parser левая часть после знака равенства находится в контексте значения, поэтому там выбирается функция Parser.
Раздельные пространства не отменяют обычные правила областей видимости и приватности. Имя должно быть доступно из текущего модуля, а при конфликте сущностей внутри одного пространства имён потребуется устранить неоднозначность или переименовать импорт.
В библиотеке нужно представить вызываемый объект типом-алиасом и предоставить функцию-фабрику. Вариант с разными именами, например Parser и create_parser, проще для чтения и обычно предпочтительнее: он снижает риск путаницы и предупреждений линтера.
Вариант с одинаковым именем использует раздельные пространства Rust и может быть уместен для тесно связанных абстракций. Его минус — ухудшение читаемости и возможные предупреждения о соглашениях именования, поэтому выбранный вариант оправдан только при явной пользе для API.
На практике чаще выбирают разные имена, если тип и функция имеют разные смысловые роли. Раздельные пространства рассматривают как возможность языка, а не как рекомендацию постоянно переиспользовать одинаковые идентификаторы.
Нет. Раздельность действует между пространствами, но конфликт двух функций или двух типов с одним именем остаётся конфликтом внутри одного пространства. Кроме того, импорт может создать коллизию, если два разных элемента попадают в один и тот же namespace.
Нет, сначала учитывается контекст использования имени. В аннотации Rust ожидает сущность из пространства типов, а в выражении — из пространства значений. Если подходящей сущности в нужном пространстве нет, компилятор не заменяет её автоматически сущностью из другого пространства.
Нет. Имя структуры доступно в пространстве типов, а её конструктор используется как значение. Поэтому структурные конструкторы участвуют в разрешении имён как значения, хотя создаются объявлением типа. Это объясняет, почему выражение создания структуры и типовая аннотация используют разные аспекты одного объявления.