Здравствуйте, D. Mon, Вы писали:
V>>А если ты имел ввиду, что "ссылочность" значения прячется от системы типов, то ты в этом случае НЕ можешь рассуждать о ссылочном типе — это будет просто некая тонкость реализации.
DM>Ничего не понял тут.
Например, если значение по ссылке нельзя изменять и ссылка не может быть нулевой, то семантика такого ссылочного типа ничем не отличается от value-типа.
А семантика у ссылочного типа одна — это хранить в себе т.н. "индекс" (или ключ) некоего значения.
Прикладное применение — организация абстракций, — работаем со значением не напрямую, а через индекс/ключ.
Если язык Rust контролирует значение этого индекса через систему типов, то поздравляю — это зачатки зависимых типов в языке.
А если нет, то это не ссылочный тип.
DM>Еще раз: что именно у тебя здесь означает Т+1?
Тоже, что у тебя — сумма типов, т.е. алгебраик {A t:T | B}, где A и B произвольные идентификаторы, например Just и Nothing.
V>>Правда, в этом случае не нужен промежуточный Box<T>, т.е. достаточно лишь Option<T>, не?
DM>Для чего достаточно?
Для семантики Option<Box<T>>.
Это твои слова?
Например, в Расте Box<T> и Option<Box<T>> представлены в памяти одинаково
Ты в них уверен?
Просто я залез, таки, в доку к Rust и вижу:
pub enum Option<T> {
None,
Some(T),
}
Обычный алгебраик, но как он реализован в Rust?
Есть же куча способов, например :
struct OptionHolder {
TypeId discriminator_; // или int discriminator_
};
struct None : OptionHolder {};
template<typename T>
class Some : OptionHolder { T value_; };
struct Option { OptionHolder * value_; };
— никакого null, значение None будет представлено ссылкой на единственное значение.
Но да, учитывая
возможность оптимизации именно такого сценария, когда у нас сумма из двух типов, причем, один из них представлен единственным значением, то кодируем это единственное значение через null, и тогда Some(T) будет хранится в памяти как T*, а None будет просто 0.
Точно ли Rust умеет оптимизировать такие сценарии до описанного?
Потому что вот с таким определением:
pub enum Option2<T> {
None,
None2,
Some(T),
}
уже такой фокус не получится.
DM>Это разные вещи получатся с разным поведением и разным представлением в памяти.
Ты же выше писал, что Option<Box<T>> и Box<T> имеют одинаковое представление?
V>>Ссылочный тип по-определению — это зависимый тип.
V>>Тот самый, угу.
V>>Т.е., тип, зависящий от значения:
V>>* валидный адрес — тип T
V>>* нулевой адрес — тип None.
DM>Стоп-стоп, во-первых у тебя тут Т уже что-то другое начал обозначать.
Почему "другое"?
T — это тип некоего значения по адресу.
DM>Во-вторых, ссылочный тип не обязательно содержит null, я ж об этом говорю. См. тот же Котлин или Раст.
Ссылочный тип обязательно содержит.
Я, таки, посмотрел на Rust только что.
Так вот, Box<T> — это НЕ ссылочный тип ни в одном из мест. Ты меня обманул. ))
Это почти точный аналог моего Strong<T>, который я давал выше.
Вот тебе твой растовский Box<T>, переписанный на Шарпе:
// ссылочный тип-указатель
class Ptr<T> where T : struct {
public T Value;
}
// тип-обертка над ссылочным типом, хранящим value-тип
struct Box<T> where T : struct {
public Ptr<T> Value { get; private set; }
public Box(T value) {
Value = new Ptr<T> { Value = value };
}
}
// тип-обертка над обычным ссылочным типом
struct Strong<T> where T : class {
public T Value { get; private set; }
public Strong(T value) {
if(value == null) throw ...;
Value = value;
}
}
Обрати внимание на
struct Box и struct Strong.
Оба типа хранят внутри себя ссылочный тип, но сами НЕ являются ссылочным типом.
Итого, гарантии соблюдаются не на уровне системы типов, а сугубо на прикладном.
Обрати внимания, что в моём случае невозможно добиться того, чтобы значение (Value) у Box или у Strong было равно null.
В Rust аналогично.
DM>Ну вот есть ссылочный тип string в условной джаве или шарпе.
DM>Мы можем написать string s = null;
Можем.
DM>Теперь напиши Option<string> по твоей задумке, да так, чтобы получилось T + 1, а не T.
String в дотнете и джаве — он уже является Option изкаробки, как любой настоящий ссылочный тип.
Если нужен тип, который достоверно не будет nullable, то используй Strong<string>.
Этот Strong<string> можно создать лишь однажды, а потом передавать м/у методами, распространяя однажды полученную гарантию.