Какова модель стоимости против ссылки в Nimrod?

ПРИМЕЧАНИЕ: я не спрашиваю о разнице между указателем и ссылкой, и для этого вопроса это совершенно не имеет значения.

Одна вещь, которую я не смог найти в явном виде - какую модель использует Nimrod?

Как C++ - где у вас есть значения и с new вы создаете указатели на данные (в таком случае переменная может содержать указатель на указатель на указатель на... на данные)?

Или как C# - где у вас есть POD-типы в качестве значений, но пользовательские объекты со ссылками (неявно)?

Я заметил только разыменование происходит автоматически, как в Go.

Rephrase. Вы определяете свой новый тип, скажем, Student (с именем, университетом, адресом). Ты пишешь:

var student ...?
  1. делать student держать фактические данные (из Student тип / класс)
  2. делать student держать указатель на данные
  3. делать 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 не прослеживается).

Другие вопросы по тегам