Мелкие пакости: время жизни переменной в Rust

Допустим, хочется получить текстовое представление типа переменной в Rust. При этом в язык входит такая замечательная функция как type_name() -> &’static str принимающая тип выдающая его тектовое обозначение. Само собой, хочет применить его не только для типа (название типа не так уж и полезно в диагностических целях), а к переменной. Логичным для C++ разработчика выглядит приблизительно следующее решение:

fn type_of<'a, T>(_: T) -> &'a str {
    unsafe { std::intrinsics::type_name::<T>() }
}

Но тут возникнет довольно занятная проблема, так как переменная становится недоступной после (с некоторыми ньюансами в зависимости от типа) получения её имени:

error: use of moved value: `*variable_name` [E0382]

После небольшой фрустрации понимаешь, что в принципе это ж фича и компилятор не должен догадываться о моих намерениях только лишь получить тип, а не реально использовать значение. Но делать что-то нужно. Единственным подходящим решением оказывается передача по ссылке (ссылке в понимании Rust, а не C++), что ожидаемо, но немного странно для C++ разработчика.

fn type_of<'a, T>(_: &T) -> &'a str {
    unsafe { std::intrinsics::type_name::<T>() }
}

Вообще, все эти мелкие пакости модели памяти постоянно преследуют при программировании на Rust. Никак не могу понять, это реально зло или я просто еще не привык и просто мыслю моделью памяти C++?

7 Comments Мелкие пакости: время жизни переменной в Rust

    1. Alexander Stavonin

      Ну я решил на него теперь не в теории, а на практике посмотреть. Думаю попробовать переписать и докрутить несколько фич в демон из своего https://github.com/astavonin/Tasks-Explorer на Rust, а то совсем проект забросил, он вроде и не запускается уже на последних OS X.
      Можно, конечно, по простому и быстрому – переписать на C++, но я решил попробовать с Rust, идет первая неделя реального использования. Выглядит Rust (особенно после обширного опыта работы с C++ разработчиками из Индии) очень удачным решением, где сильно налажать относительно сложно.

      Reply
  1. LordJZ

    > Логичным для C++ разработчика выглядит приблизительно следующее решение

    Для С++ это неявно создает копию объекта – очень плохое поведение, и Rust молодец, что учит правильно передавать параметры. Это не пакость и ничего странного здесь нет.

    Reply
    1. Alexander Stavonin

      Да, кстати, верное замечание, спасибо!

      Подумал, немного. Тут, скорее, дело в терминологии еще. В Rust (насколько я его сейчас понимаю) нет ссылок в том виде, в котором они есть в C++. Тоесть конструкция val: &T – это скорее передача адреса, если смотреть с точки зрения C++, так что это в изрядной степени ограничение системы типов Rust и некий вид пакости.

      Reply
  2. LordJZ

    Ссылки/значения здесь вообще не при чем. Для безопасности важно владение объектом, копирование объекта и удаление объекта. Семантика ссылок и значений в Rust управляется трейтом Copy.

    Вот примерно эквивалентные определения в Rust и C++:

    _: T
    T&& _

    _: &T
    T const& _

    То есть, в первом случае вы попытались уничтожить объект для получения его типа, что не имеет особого смысла, о чём вас Rust и уведомил. Второй же случай — передача константной ссылки вместо копирования — вполне каноничен для С++.

    Reply
    1. Alexander Stavonin

      Это верно. Но на сколько я понимаю, Rust не сможет решить что требуется, копия объекта или передача по ссылке, что для C++ работает, если функция ожидает на вход ссылку. Или я что-то упускаю в Rust?

      Reply
      1. LordJZ

        Borrow больше похож на указатели из C++, где всегда надо указывать взятие адреса: f(&a)
        Я не уверен, оптимизирует ли компилятор borrow в передачу по значению для простых Copy типов, но я думаю это вполне осуществимо. Но ни семантика, ни работа программы от этого не меняются.

        Reply

Leave a Reply