LINUX.ORG.RU

Rust и типобезопасность

 ,


3

7

Решил я тут немного изучить Rust, чтобы не состариться, так и не вкусив плодов современной цивилизации.

Rust рекламируется как типобезопасный ЯП, и я подумал, что нужно это как-то проверить, увидеть, как говорится, своими глазами. На примере всяких диких, но вполне встречающихся конверсий типа из вещественного в целое. Тема важная, из-за подобных штук иногда ракеты падают, так что если ЯП заведомо типобезопасный, он должен такие ситуации корректно обрабатывать или бить по рукам, хотя бы в рантайме.

Почитав документацию:

The as keyword does safe casting

Набросал такой примерчик:

fn main() {
        let a: f64 = std::f64::MAX;  // данное значение просто пример "большого числа"
        let b = a as i64;
        println!("{}", b);
}

Вопросы:

1) С какой стати это вообще компилируется?

2) Да, f64 и i64 нужно одно и то же количество битов для хранения значения, ну и что?

3) Почему результат меняет знак?

Представьте, что какой-то тех. процесс идет, и определенный параметр нельзя изменять скачкообразно, иначе физически система (по крайней мере, один из компонентов) выйдет из строя (встанет в раскоряку, взорвется, выпустит токсичный газ, убивающий 100тыс. населения; нужное подчеркнуть). И вот значение в f64 положительное и увеличивается постепенно, с i64 все вроде в порядке (и тестирование проходит на тестовых значениях), и вдруг хренак! и уже -9223372036854775808

Как так?

★★★★★

1) С какой стати это вообще компилируется?

а чего не должно?

        let b = a as i64;

Что ты хочешь сделать этим идиотизмом из сишечки и прочего скама? Взять целую часть числа? Так используй соответсвующие функции.

От идиотизма программиста не защититься.

compasses
()

встанет в раскоряку, взорвется, выпустит токсичный газ, убивающий 100тыс. населения; нужное подчеркнуть

нужное подчеркнуть

Так-так, дайте подумать…

anonymous
()

в rustonomicon написано, что в современном rust этот код не просто приводит к неожиданному результату, а вообще является UB

casting from a float to an integer will round the float towards zero. NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the target integer type. This includes Inf and NaN. This is a bug and will be fixed.

Lrrr ★★★★★
()

А ведь могли в as включить выделение целого из вещественного, без округления что бы было явное поведение. :D

LINUX-ORG-RU ★★★★★
()
let b = a as i64;

Я руст не знаю, я жабокодер, но разве это не операция явного приведения типа, в IDE у тебя тут должен быть ворнинг, наверное и компилятор тоже может его выводить.

  1. Да, f64 и i64 нужно одно и то же количество битов для хранения значения, ну и что?

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

anonymous
()

Скастуй сюда главного растомана лора

LINUX-ORG-RU ★★★★★
()

Что-то не вижу «The as keyword does safe casting» в документации.

А вот это вижу: "Casting from a float to an integer will round the float towards zero

NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the target integer type. This includes Inf and NaN. This is a bug and will be fixed."

https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions

red75prim ★★★
()
Ответ на: комментарий от RazrFalcon

В педерасте язык не имеет смысл без компилятора

anonymous
()
Ответ на: комментарий от compasses

Что ты хочешь сделать этим идиотизмом из сишечки и прочего скама?

А очень просто. Есть некоторый физический параметр, который используется в разных модулях системы. Модуль, который разрабатываешь ты, требует повышенной точности при вычислениях или большого диапазона допустимых значений. Модуль, который принимает i64 разработан другими людьми. Он не требует повышенной точности, хорошо отлажен на легаси системе, и более того, он успешно работает с твоим модулем в тестовых условиях.

Примерно тоже самое делали, когда случился былинный провал со взрывом Ariane 5, и было это почти 25 лет назад, задолго до появления Rust.

seiken ★★★★★
() автор топика
Ответ на: комментарий от anonymous

наверное и компилятор тоже может его выводить.

я пробовал на https://play.rust-lang.org/ , никаких ворнингов не выводит

seiken ★★★★★
() автор топика
Ответ на: комментарий от seiken

Дык а вообще неясно как это обрабатывать, понятно что у плавающей точки диапазон шире чем у целого. Если возникает такая ситуация, это косяк в проектировании?

Кидать ворнинги на все такие приведения? Но Вы же сами написали приведение ручками…

AntonI ★★★★
()

Ответ википедии на твой вопрос

Type safety is sometimes alternatively considered to be a property of a computer program rather than the language in which that program is written; that is, some languages have type-safe facilities that can be circumvented by programmers

DonkeyHot ★★★★★
()

Компилируйте на nightly с помощью cargo rustc --release -- -Z saturating-float-casts

Однозначного решения по этому вопросу пока не приняли, поэтому флаг компиляции нестабильный.

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)
Ответ на: комментарий от seiken

по хорошему float в int вообще не должен кастоваться по дефолту. они хоть и низываются «числом», но у них мало общего.

alysnix ★★★
()
Ответ на: комментарий от anonymous

например в жабе придется бинарное представления флоата в интерджер окольными путями делать, через сериализаци-десериализацию,

Нет. В Java для этого есть API в java.lang.Double.

Bass ★★★★★
()
Ответ на: комментарий от red75prim

Компилируйте на nightly

Вся суть секты

anonymous
()
Ответ на: комментарий от LINUX-ORG-RU

llvm никто переписывать не собирается, насколько мне известно.

Ну а оператор as - это вообще косяк дизайна языка. Надеюсь его сделают в итоге unsafe, как и положено.

RazrFalcon ★★★★★
()
Ответ на: комментарий от red75prim

Я всё же надеюсь что они таки реализуют TryFrom для float. Сейчас приходится num-traits использовать.

RazrFalcon ★★★★★
()

У меня есть другой пример из моей личной практики.

Предположим, написали на C++ дикий код с шаблонами. Почти наверняка, большая часть плюсового кода нифига не проверена, потому что проверяется по факту в момент инстанцирования шаблона, а инстанцирования могут сильно отличаться друг от друга.

В случае Rust аналогичный код, скорее всего, будет состоять из трейтов и генериков. Важное отличие от С++ - растовский код будет проверен до инстанцирования генериков!

Чуешь разницу? Как опытный плюсовик ты должен был бы воскликнуть сейчас: «Эврика!»

dave ★★★★★
()
Ответ на: комментарий от dave

дикий код с шаблонами

может просто написать недикий и без шаблонов?

alysnix ★★★
()
Ответ на: комментарий от dave

Чуешь разницу? Как опытный плюсовик ты должен был бы воскликнуть сейчас: «Эврика!»

Это как предлагать опытному плюсовику джаву и думать, что он будет счастлив. Разработчики Rust даже специализацию полноценно сделать не могут.

Serral
()

Я такое даже в сишечке не делаю.

WatchCat ★★★★★
()
Ответ на: комментарий от dave

Чуешь разницу? Как опытный плюсовик ты должен был бы воскликнуть сейчас: «Эврика!»

Я не опытный плюсовик, так что кричать не буду в любом случае.

В случае Rust аналогичный код, скорее всего, будет состоять из трейтов и генериков. Важное отличие от С++ - растовский код будет проверен до инстанцирования генериков!

А о каких проверках речь идет? Не мешало бы примерчик.

seiken ★★★★★
() автор топика
Ответ на: комментарий от compasses

От идиотизма программиста не защититься.

Я думаю суть претензии в документации которая слишком свободно использует слово safe. Это слишком широко используемое слово и не стоит без оговорок так писать.

Но автор ссылочку на док то не скинул, насколько это все официально?

Вот официальный док

https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics

Написано прямо жирным шрифтом

  • Casting from a float to an integer will round the float towards zero
    • NOTE: currently this will cause Undefined Behavior if the rounded value cannot be represented by the target integer type. This includes Inf and NaN. This is a bug and will be fixed.
vertexua ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

Так может пора отвязаться от LLVM и написать уже Rust на Rust по взрослому?

Нет, шашечки не нужны. Лучше ехать

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Нет, шашечки не нужны. Лучше ехать

Вот поэтому в системном программировании все и пишут на C или C++. До тебя уже начинает доходить.

Разорванный Флакон

anonymous
()
Ответ на: комментарий от anonymous

Вот поэтому в системном программировании все и пишут на C или C++

Это те, кто еще в неосознанке или с легаси.

Царь, ты? Где блог написаный с нуля на плюсах?

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от vertexua

Это те, кто еще в неосознанке или с легаси.

А остальные на чем пишут?

Serral
()
Ответ на: комментарий от vertexua

Царь, ты? Где блог написаный с нуля на плюсах?

Я смотрю тебе до сих пор везде цари мерещатся, видимо, неплохо он так расширил, кхм, твои горизонты познания.

Разорванный Флакон

anonymous
()
Ответ на: комментарий от seiken

В C++ сначала происходит инстанциирование, а затем компиляция. В расте же сначала проверяются трейты, а потом уже происходит инстанциирование. Из-за чего ошибки в шаблонном коде намного более вменяемые.

struct MyType(i32);

fn max_value<T>(items: &[T]) {
    println!("{:?}", items.iter().max());
}

fn main() {
    max_value(&[MyType(1), MyType(2), MyType(3)]);
}
error[E0277]: the trait bound `T: std::cmp::Ord` is not satisfied
 --> src/main.rs:4:35
  |
3 | fn max_value<T>(items: &[T]) {
  |              - help: consider restricting this bound: `T: std::cmp::Ord`
4 |     println!("{:?}", items.iter().max());
  |                                   ^^^ the trait `std::cmp::Ord` is not implemented for `T`
  |
  = note: required because of the requirements on the impl of `std::cmp::Ord` for `&T`

error[E0277]: `T` doesn't implement `std::fmt::Debug`
 --> src/main.rs:4:22
  |
3 | fn max_value<T>(items: &[T]) {
  |              - help: consider restricting this bound: `T: std::fmt::Debug`
4 |     println!("{:?}", items.iter().max());
  |                      ^^^^^^^^^^^^^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
  |
  = help: the trait `std::fmt::Debug` is not implemented for `T`
  = note: required because of the requirements on the impl of `std::fmt::Debug` for `&T`
  = note: required because of the requirements on the impl of `std::fmt::Debug` for `std::option::Option<&T>`
  = note: required by `std::fmt::Debug::fmt`

error: aborting due to 2 previous errors

vs

#include <iostream>

struct MyType { int d; };

template<typename T>
void max_value(const std::vector<T> &items)
{
    std::cout << std::max_element(items.begin(), items.end()) << std::endl;
}

int main(int argc, char *argv[])
{
    const std::vector<MyType> items{ {1}, {2}, {3} };
    max_value(items);
    return 0;
}

Вывод копипасть не будут, ибо не влезет.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Вывод - на C++ простой и лаконичный код, а на Расте какая-то портянка текста, да еще и сообщение об ошибке.

Разорванный Флакон

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.