Как стать автором
Обновить

Комментарии 33

Осталось только научить датчики на МК сообщать, в цельсиях они температуру передают или в фаренгейтах.

Ну да, не панацея. Но если у нас функция принимает какое-то абстрактное числовое значения, то "потерять бдительность" довольно легко. Если же компилятор заставит приводить к тем или иным величинам, то шанс осмысленно их обработать увеличивается.

Жуть полезные типажи. Особенно спасают при приведении заимствованных типов, если имена типов разные, но внутри по факту одно и то же:
pub struct Vertex([f32; 3]);

impl <'a> From<&'a cgmath::Matrix<f32>> for &'a [Vertex] {
	fn from(a: &'a cgmath::Matrix<f32>) -> &'a [Vertex] {
		use std::mem;
		use std::slice;		
		unsafe {
			slice::from_raw_parts(
				a as *const _ as *const Vertex, 
				mem::size_of::<cgmath::Matrix<f32>>() / mem::size_of::<Vertex>()
			)
		}
	}
}

А можно ли в Расте делать такие выкрутасы?


    auto distance = 384_400 * kilo(meter);
    auto speed = 299_792_458  * meter/second;

    Time time;
    time = distance / speed;
    writefln("Travel time of light from the moon: %s s", time.value(second));

enum inch = 2.54 * centi(meter);
enum mile = 1609 * meter;
writefln("There are %s inches in a mile", mile.value(inch));
НЛО прилетело и опубликовало эту надпись здесь

Давайте оба варианта :-)

Хм… разве штуки типа kilo(meter) или meter/second прямо один в один сделать получится? С вещами типа si!"384_400 km" и правда не должно быть проблем.

НЛО прилетело и опубликовало эту надпись здесь

Не соображу как с пустой структурой извернуться, но додумался до такого:


trait SiUnit {
    type ValueType: Mul<Self::ValueType, Output = Self::ValueType> + From<u64>;

    fn get(&self) -> Self::ValueType;
    fn set(&mut self, value: Self::ValueType);
}

fn kilo<T: SiUnit>(mut val: T) -> T {
    let v = val.get();
    val.set(v * 1000.into());
    val
}

struct Metr(u64);

impl SiUnit for Metr {
    type ValueType = u64;

    fn get(&self) -> Self::ValueType {
        self.0
    }

    fn set(&mut self, value: Self::ValueType) {
        self.0 = value;
    }
}

const metr: Metr = Metr(1);

let a = kilo(metr);

Получается очень близко к D, хотя про реализацию time.value(second)ещё придётся подумать.


Может уже плохо соображаю на ночь глядя, но кажется, что сильно красивее/короче это дело не реализовать. В таких вещах шаблоны D (или С++) смотрятся элегантнее, особенно, если вынести за скобки обработку ошибок. С другой стороны, все эти ужасы (в расте) можно спрятать в библиотеку.

Там есть ещё такой нюанс: meter/second возвращает тип "метры в секунду", а в distance/speed метры сокращаются и получается тип "секунды". Можно ли в Расте также выводить новые типы из библиотечных?


enum euro = unit!(double, "C"); // C is the chosen dimension symol (for currency...)
НЛО прилетело и опубликовало эту надпись здесь

into — это же то же фактически автоматическое приведение типов. Его имело бы смысл вынести на уровень языка, чтобы:


  1. Не заниматься однообразной ручной работой.
  2. Не путаться, когда одни функции приводят тип, а другие — нет.

В том же D можно перегрузить метод opCast, и он будет вызываться, например, автоматически в условиях:


struct X
{
    bool opCast( T : bool )( )
    {
        return false;
    }
}

writeln( X() ? 1 : 2 );

Правда условиями всё и ограничивается, для остальных случаев приведение опять приходится писать явно :-(


struct X
{
    int x = 5;
    int opCast( T : int )( )
    {
        return x;
    }
}

writeln( 1 + cast(int) X() );
into — это же то же фактически автоматическое приведение типов.

Не совсем. Разница как раз в том, что это приведение будет работать именно в конкретных функциях, а не везде:


struct S1 {}
struct S2 {}

impl From<S1> for S2 {
    fn from(_: S1) -> Self { S2{} }
}

let s1 = S1 {};
let s2: S2 = s1/*.into()*/; // error

И это уже ответственность функции принимать ли типы, которые можно преобразовать или нет. И хорошей практикой считается не лепить это везде, а только в специальных случаях вроде String/&str.


В общем, можно спорить, но мне отсутствие автоматического приведения типов как раз очень нравится в расте.

НЛО прилетело и опубликовало эту надпись здесь

Нет, утиная типизация и приведение типов — разные вещи. Into в зависимости от контекста приводит к разным типам. Так что плохого в автоматическом приведении типов?

НЛО прилетело и опубликовало эту надпись здесь

Могу, но зачем?

Примерно затем же, зачем в С++ есть explicit (только наоборот).

Я C++ не трогал лет десять. Не напомните зачем там этот explicit?

Как раз для того, чтобы запретить неявные преобразования:


struct S {
    explicit S(int) {}
};

void foo(S) {}

//foo(10); // Error
foo(S(10));

И зачем их запрещать?

Я не знаю как ответить, чтобы не вызвать флейм. На языке вертится "примерно затем же, зачем нужна статическая типизация", но очевидно, такой ответ не устроит. (:


Можно поискать аргументацию зачем explicit был добавлен (причём в стандарте 11 года его действие расширили и на операторы). Сильно убедительных примеров у меня нет, могу только повторить, что меня вполне устраивает когда ситуация когда неявных приведений вообще нет в языке. Одна из причин — сделать создание "дорогого" объекта более явным.

Для "дорогого" объекта достаточно не реализовывать implicit кастинг. Но для тех же единиц измерения — какая разница какую единицу измерения принимает функция, если у меня есть температура лишь в градусах цельсия?

Там есть ещё такой нюанс: meter/second возвращает тип "метры в секунду", а в distance/speed метры сокращаются и получается тип "секунды".

Так можно и в расте:


struct Meters {}
struct Seconds {}
struct MetersPerSecond {}

impl Div<Seconds> for Meters {
    type Output = MetersPerSecond;
    fn div(self, _: Seconds) -> Self::Output { MetersPerSecond {} }
}

impl Div<MetersPerSecond> for Meters {
    type Output = Seconds;
    fn div(self, _: MetersPerSecond) -> Self::Output { Seconds {} }
}

// Аннотации типов просто чтобы показать, что они действительно такие.
let m: Meters = Meters {};
let s: Seconds = Seconds {};
let ms: MetersPerSecond = m / s;
let s: Seconds = m / ms;

Можно ли в Расте также выводить новые типы из библиотечных?

А имя типа в D после unit! руками написать можно? В смысле, это подставляется какой-то заранее известный тип или создаётся новый? Или из переданной строки имя типа и сформируется? В принципе, в расте можно и так и так.

Не, там фишка в том, что руками объявляются лишь базовые единицы измерения, а производные собираются автоматически. На этапе компиляции тип специфицируется строкой. грубо говоря unit!"meter/second/second" — тип ускорения.

Любопытно. А можно всё-таки на пальцах объяснить как это работает? Ну то есть, есть у нас типы meter и second. Пишем unit!"meter/second" и создаётся тип meter/second являющийся результатом деления метров на секунды?


На первый взгляд, не вижу причин почему такое не получится изобразить на расте, хотя придётся прибегать к ("нестабильным") процедурным макросам.

У нас есть значения meter типа Unit!("meter",1) и second типа Unit!("second",1). Операция деления перегружена таким образом, что она берёт размерности обоих операндов, вычисляет итоговую размерность и возвращает соответствующий тип. Например, для meter/second/second будет тип Unit!("meter",1,"second",-2). Ну, я бы реализовал это именно так. Конкретно в той библиотеке реализовано как-то замороченно. Возможно, чтобы можно было вычислять размерности не только во время компиляции, но и в рантайме.

На rust можно реализовать числа во время компиляции:
https://habrahabr.ru/post/310572/


Наверно, можно по аналогии сделать описываемую структуру многомерной — под три измерения. Только не очень понятно, что делать с нецелыми или отрицательными степенями (вроде бы можно представлять их как дробь и ещё знак впереди, но это кажется извращением).


P.S. если что, я ни rust ни D не знаю.

Или я что-то не понял, или это просто динамическая типизация, реализованная уже поверх системы типов D.


Т.е. D же видит везде один и тот же тип, просто у этого типа там уже разные поля с закодированными в строках "типами", а во время компиляции эта штука работает только за счет CTFE, нет?

Скорее автоматическое выведение типов, реализованное, через ctfe.

Мне не хватает знания D, что бы толком понять как именно это работает, но насколько я понимаю, когда в ржавчине стабилизируют CTFE, примерно такое же вполне можно будет сделать, если еще и макросы подключить к вопросу.

Скорее всего тут ещё необходим.и eval времени компиляции. То есть создавать не только значения, но и типы. Или макросы в расте покрывают это?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории