Комментарии 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 — это же то же фактически автоматическое приведение типов. Его имело бы смысл вынести на уровень языка, чтобы:
- Не заниматься однообразной ручной работой.
- Не путаться, когда одни функции приводят тип, а другие — нет.
В том же 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 года его действие расширили и на операторы). Сильно убедительных примеров у меня нет, могу только повторить, что меня вполне устраивает когда ситуация когда неявных приведений вообще нет в языке. Одна из причин — сделать создание "дорогого" объекта более явным.
Там есть ещё такой нюанс: 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, примерно такое же вполне можно будет сделать, если еще и макросы подключить к вопросу.
Передача намерений