Поскольку, как представляется, существует некоторая путаница (как в исходном вопросе, так и в комментарии he_the_great) относительно неизменности, я подумал, что добавлю в сторону.
Когда вы говорите immutable int i = 42
, вы говорите, что я не буду изменен, а не то, что это значение известно во время компиляции. immutable
на самом деле является модификатором типа и создает новый тип. immutable T
является сокращением для immutable(T)
. immutable(T)
создает T, который никогда не может быть изменен, то есть, если вы прочитаете значение, а затем вызовете функцию, значение будет таким же. Сравните это с const(T)
, что обеспечивает более слабую гарантию того, что этот экземпляр типа не будет изменен, но кто-то может иметь изменяемый доступ к нему в другом месте, поэтому, если вы читаете значение и затем вызываете функцию, вы не можете принять значение будет таким же.
В общем, immutable(T) != T
. Однако существуют определенные ситуации, когда они неявно конвертируются друг в друга. Если T является типом, который, как говорят, не имеет "изменяемой косвенности", например. То есть, если я передам функции immutable(int)
, они получат копию - эта функция не сможет изменить переданное мной значение, так как оно копируется - если система типов не допустит этого , это было бы просто раздражающим без каких-либо дополнительных гарантий, поэтому система типов D позволяет это. Однако, если я передам неизменяемый (int *), то может быть изменен вызывающей функцией . В случае структур, если у какого-либо члена есть изменяемая косвенность, то говорят, что структура также имеет его.
Таким образом, чтобы отвлечься от теории и вернуться к более практическим вопросам, совершенно неверно, что неизменные ценности должны быть известны во время компиляции, и что нет хорошего способа их создания. Однако единственная мутация может происходить внутри конструктора. Для простых скалярных типов это довольно очевидно:
immutable(int) i = rand();
Но как насчет чего-то вроде объекта? Ну, для построения типа T мы используем
auto t = new T();
поэтому для построения типа неизменяемого (T) мы используем
auto t = new immutable(T)();
вот более полный маленький пример
class Useless
{
int i;
this(int i)
{
this.i = i;
}
}
int main(string[] args)
{
auto o = new immutable(Useless)(cast(int) args.length);
//o.i = 17; error
return o.i; // fine
}
Как видите, мутация может происходить внутри конструктора. Вы можете читать переменные-члены, но не можете их записывать (неизменяемый является транзитивным; то есть каждый член (и каждый член-члены) становится неизменным, если это делает родитель. Вы можете вызывать методы, только если они помечены как const
.
Я прошу прощения за бессвязные сообщения, но вижу, что многие люди смущены этой темой.