Какова модель стоимости против ссылки в Nimrod?
ПРИМЕЧАНИЕ: я не спрашиваю о разнице между указателем и ссылкой, и для этого вопроса это совершенно не имеет значения.
Одна вещь, которую я не смог найти в явном виде - какую модель использует Nimrod?
Как C++ - где у вас есть значения и с new
вы создаете указатели на данные (в таком случае переменная может содержать указатель на указатель на указатель на... на данные)?
Или как C# - где у вас есть POD-типы в качестве значений, но пользовательские объекты со ссылками (неявно)?
Я заметил только разыменование происходит автоматически, как в Go.
Rephrase. Вы определяете свой новый тип, скажем, Student
(с именем, университетом, адресом). Ты пишешь:
var student ...?
- делать
student
держать фактические данные (изStudent
тип / класс) - делать
student
держать указатель на данные - делать
student
держать указатель на указатель на данные
Или некоторые из этих пунктов невозможны?
2 ответа
По умолчанию модель передает данные по значению. Когда вы создаете переменную определенного типа, компилятор выделяет в стеке необходимое пространство для переменной. Что и ожидается, поскольку Nim компилируется в C, а сложные типы являются просто структурами. Но, как и в C или C++, вы также можете иметь указатели. Здесь ptr
ключевое слово, чтобы получить небезопасный указатель, в основном для взаимодействия с кодом C, и есть ref
чтобы получить безопасную ссылку для сборки мусора (как описано в разделе " Ссылки" и "Типы указателей " руководства Nim).
Тем не менее, обратите внимание, что даже когда вы указываете proc
чтобы передать переменную по значению, компилятор может решить передать ее внутренне по ссылке, если считает, что это может ускорить выполнение и в то же время безопасно. На практике единственный раз, когда я использовал ссылки, это когда я экспортировал типы Nim в C и должен был убедиться, что и C, и Nim указывали на одну и ту же память. Помните, что вы всегда можете проверить сгенерированный код C в nimcache
каталог. Вы увидите, что var
Параметр в proc - это просто указатель на его C-структуру.
Вот пример типа с конструкторами, которые должны быть созданы в стеке и переданы по значению, и соответствующий указатель, такой как версия:
type
Person = object
age: int
name: string
proc initPerson(age: int, name: string): Person =
result.age = age
result.name = name
proc newPerson(age: int, name: string): ref Person =
new(result)
result.age = age
result.name = name
when isMainModule:
var
a = initPerson(3, "foo")
b = newPerson(4, "bar")
echo a.name & " " & $a.age
echo b.name & " " & $b.age
Как видите, код по сути такой же, но есть некоторые отличия:
- Типичный способ дифференциации инициализации - использовать init для типов значений и new для ссылочных типов. Также обратите внимание, что собственная стандартная библиотека Nim ошибочна в этом соглашении, так как часть кода предшествует ему (например, newStringOfCap не возвращает ссылку на строковый тип).
- В зависимости от того, что на самом деле делают ваши конструкторы,
ref
версия позволяет вернутьnil
значение, которое вы можете рассматривать как ошибку, в то время как конструктор значения вынуждает вас вызвать исключение или изменить конструктор для использования формы var, упомянутой ниже, чтобы вы могли вернуть логическое значение, указывающее на успех. Отказ имеет тенденцию рассматриваться по-разному. В C-подобных языках существует явный синтаксис для доступа либо к значению памяти указателя, либо к значению памяти, указанному им (разыменование). В Nim есть и пустая нижняя запись (
[]
). Тем не менее, компилятор попытается автоматически установить их, чтобы избежать загромождения кода. Следовательно, пример не использует их. Чтобы доказать это, вы можете изменить код на:echo b[].name & " " & $b[].age
Который будет работать и компилироваться как ожидалось. Но следующее изменение приведет к ошибке компилятора, потому что вы не можете разыменовать не ссылочный тип:
echo a[].name & " " & $a[].age
Современная тенденция в сообществе Nim - избавиться от однобуквенных префиксов, чтобы отличать значения от ссылочных типов. В старом соглашении вы бы имели
TPerson
и псевдоним для ссылочного значения какPPerson = ref TPerson
, Вы можете найти много кода, все еще используя это соглашение.- В зависимости от того, что именно ваш объект и конструктор должны сделать, вместо того, чтобы иметь
initPerson
возвращая значение, вы также можете иметьinit(x: var Person, ...)
, Но использование неявногоresult
переменная позволяет компилятору оптимизировать это, так что это гораздо больше вкусовых предпочтений или требований прохожденияbool
для звонящего.
Это может быть либо.
type Student = object ...
примерно эквивалентно
typedef struct { ... } Student;
в С, в то время как
type Student = ref object ...
или же
type Student = ptr object ...
примерно эквивалентно
typedef struct { ... } *Student;
в С (с ref
обозначает ссылку, которая отслеживается сборщиком мусора, а ptr
не прослеживается).