В Rust мне нужен числовой тип со свойством иметь домен, симметричный относительно 0. Если число n является допустимым значением, тогда число -n должно также быть действительным. Как обеспечить безопасность типов при инициализации и арифметических операциях? Как лучше всего реализовать модульную арифметику и арифметику с насыщением для типа?
Самый простой пример проблемы:
type MyNumber = i8; // Bound to domain (-100, 100)
fn main() {
let a = MyNumber(128); // Doesn't panic when 128 > 100
}
Есть несколько соображений, и я пробовал разные решения. Я буду избегать общего программирования для приведенных ниже примеров:
Основание типа на перечислении гарантирует, что возможными значениями будут только допустимые значения. Это очень быстро становится беспорядочным:
enum MyNumber { One, Two, ... } impl MyNumber { fn convert(i8) -> MyNumber { match { 1 => MyNumber::One, 2 => MyNumber::Two, ... } } }
Предоставьте метод, который проверяет параметры перед настройкой полей, связанный с учебником функция. Это не мешает назначению с помощью конструктора структуры.
Проверяйте операнды (и принудительно исправляйте их) всякий раз, когда происходит операция. Это кажется разумным, но требует, чтобы каждый метод повторял код проверки.
extern crate num; use num::Bounded; use std::cmp; struct MyNumber { val: i8, } impl Bounded for MyNumber { fn max_value() -> Self { MyNumber { val: 65 } } fn min_value() -> Self { MyNumber { val: -50 } } } impl MyNumber { fn clamp(&mut self) { self.val = cmp::min(MyNumber::max_value().val, cmp::max(MyNumber::min_value().val, self.val)) } fn add(&mut self, mut addend: Self) { self.clamp(); addend.clamp(); //TODO: wrap or saturate result self.val = self.val + addend.val } } fn main() { let mut a = MyNumber { val: i8::max_value() }; let b = MyNumber { val: i8::min_value() }; a.add(b); println!("{} + {} = {}", MyNumber::max_value().val, MyNumber::min_value().val, a.val); }
Ни одно из вышеперечисленных решений не является очень элегантным - в некоторой степени это связано с тем, что они являются реализациями прототипов. Должен быть более чистый способ ограничить домен числового типа!
Какая комбинация типа и свойств будет проверять границы, использовать их для арифметики модульности / насыщенности и легко преобразовывать в числовой примитив?
РЕДАКТИРОВАТЬ: этот вопрос был помечен как дубликат гораздо более старого вопроса из 2014. Я не верю, что эти вопросы совпадают на том основании, что Rust был pre alpha, а в версии 1.0 были внесены значительные улучшения в язык. Разница в большем масштабе, чем между Python 2 и 3.