All streams
Search
Write a publication
Pull to refresh
88
0
Александр Мещеряков @freecoder_xx

Rust разработчик

Send message

Для определения того, какой из двух данных цветов светлее, это и не важно )

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

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

Надо было Gray50, Gray66 и Gray75 назвать, тогда проблемы бы не было :)

async/await, тем более что фича уже появилась в релизной версии компилятора

Эмм… Это в какой, не подскажете? )

Спасибо! Вы озвучили и мои мысли тоже относительно этого фильма. Как ни странно, на Хабре оказалась самая трезвая аудитория, в сравнении с другими ресурсами, где также обсуждается "Чернобыль".

Дело в том, что типы (по крайней мере в Rust) — это объекты времени компиляции. Тут нужен способ некоего "структурного запоминания", если таковой возможен.

В Rust по идее можно развить функционал ассоциированных типов для достижения нечто подобного. Собственно, первый вариант "замыкания" пытался их использовать, но ограничения системы типов текущего Rust не позволили.

На первые вопросы ответил ниже


значит ли данное ограничение, что составить дерево из произвольного количества компонент на основе, например, пользовательского input'а с таким подходом будет невозможно?

Нет, не значит )

Вариант с вектором методов:


struct Component {
    node: Box<dyn Any>,
    model: Box<dyn Any>,
    methods: Vec<Box<dyn Any>>,
}

impl Component {
    fn new<Model: 'static + Debug>(node: Node<Model>, model: Model) -> Self {
        let use_model_a: fn(&Component) = |comp: &Component| {
            let model = comp.model.downcast_ref::<Model>().unwrap();
            println!("Model: {:?}", model);
            comp.node.downcast_ref::<Node<Model>>().unwrap().use_model();
        };

        let use_model_b: fn(&Component, i32) -> i32 = |comp: &Component, x: i32| {
            comp.model.downcast_ref::<Model>().unwrap();
            x
        };

        Self {
            node: Box::new(node),
            model: Box::new(model),
            methods: vec![Box::new(use_model_a), Box::new(use_model_b)],
        }
    }

    fn use_model_a(&self) {
        (self.methods[0]
            .downcast_ref::<fn(&Component)>()
            .unwrap()
        )(self)
    }

    fn use_model_b(&self, x: i32) -> i32 {
        (self.methods[1]
            .downcast_ref::<fn(&Component, i32) -> i32>()
            .unwrap()
        )(self, x)
    }
}

Запустить


Можно даже кастить функциональные указатели напрямую. Они одного размера, так что по идее с этим не должно быть проблем, хотя я на 100% не уверен в безопасности такого кода.

Ну, 'static-типы — это такие типы, определение которых не содержит ссылок или содержит только статические ссылки (с лайфтаймом 'static). Да, это касается только лайфтаймов, никаких ограничений на задание самих значений полей нет.


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


struct Component {
    node: Box<dyn Any>,
    model: Box<dyn Any>,
    method_closure: fn(&Component, MethodArgs) -> MethodResult,
}

enum MethodArgs {
    UseModelA,
    UseModelB(i32),
}

enum MethodResult {
    UseModelA(()),
    UseModelB(i32),
}

impl Component {
    fn new<Model: 'static + Debug>(node: Node<Model>, model: Model) -> Self {
        let method_closure = |comp: &Component, args: MethodArgs| {
            match args {
                MethodArgs::UseModelA => MethodResult::UseModelA(
                    comp.use_model_a_impl::<Model>()
                ),
                MethodArgs::UseModelB(x) => MethodResult::UseModelB(
                    comp.use_model_b_impl::<Model>(x)
                ),
            }
        };

        Self {
            node: Box::new(node),
            model: Box::new(model),
            method_closure,
        }
    }

    fn use_model_a(&self) {
        (self.method_closure)(self, MethodArgs::UseModelA);
    }

    fn use_model_b(&self, x: i32) -> i32 {
        match (self.method_closure)(self, MethodArgs::UseModelB(x)) {
            MethodResult::UseModelB(x) => x,
            _ => panic!("Wrong method_closure response type"),
        }
    }

    fn use_model_a_impl<Model: 'static + Debug>(&self) {
        let model = self.model.downcast_ref::<Model>().unwrap();
        println!("Model: {:?}", model);
        self.node.downcast_ref::<Node<Model>>().unwrap().use_model();
    }

    fn use_model_b_impl<Model: 'static>(&self, x: i32) -> i32 {
        self.model.downcast_ref::<Model>().unwrap();
        x
    }
}

Запустить

У личного домика с двориком — тоже много преимуществ. Но попробуйте-ка заведите себе такой не в деревне, а в крупном городе, поближе к центру.

Так значит все-таки будет коммунизм?

If there is no W2 (that is, the sequence ends with W1), or if W2 is not between 0xDC00 and 0xDFFF, the sequence is in error.
Terminate.

Т.е. там попросту есть ограничения на значения во второй части пары.


При этом последующее слово обязательно должно содержать 110111 в старших разрядах. Если это не так, то последовательность невалидна.

Этого я там не нашел.



0xDC00 = 1101110000000000
0xDFFF = 1101111111111111

возможно этой логикой должен обладать какой-то другой класс/объект/модуль

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


Иначе обстоит дело в ООП языках. Там есть соблазн просто унаследоваться. И так делают достаточно часто — используют наследование именно для расширения, а о том, что при этом еще и полиморфизм подтипов начинает работать, не думают. Иногда это приводит к проблемам.


Square даже по определению подтипом Rectangle не является

И почему это? Квадрат — частный случай прямоугольника. То есть там, где мы ожидаем прямоугольник, всегда должно быть можно подставить квадрат. Вы с этим спорите?

Наследование и полиморфизм подтипов


Наследование удобно использовать для синтаксического расширения существующего класса: когда потомок имеет те же поля и методы, что и предок + свои. Далеко не всегда желательно, чтобы при этом потомок был подтипом предка. И наоборот, часто для образования подтипа приходится использовать наследование и тянуть в потомка все содержимое предка, хотя это и не нужно.


Вот вам пример для иллюстрации проблемы: есть класс Rectangle и Square. Ясно, что Square должен быть подтипом Rectangle, но наследование реализации при этом не желательно. Эффективная реализация Square должна хранить только поле a, вместо двух полей a и b, которые она получила бы в наследство от Rectangle.

Это вряд ли. Проблема ООП (в том виде как оно реализовано в мейнстримовых языках) как минимум в том, что в нем смешивается наследование с образованием подтипов.

Ну, через текст статьи уже проступает некоторая "ржавчина" ) В Rust нет ООП (в смысле наследования данных и образования подтипов с помощью наследования), нет культуры обязательного наличия геттеров/сеттеров, уровень доступа к полям можно ограничивать видимостью в модуле и т.п. Rust снимает большинство претензий автора.

Можете предоставить ссылку на то, что стандарт utf16 как-то ограничивает значение второго числа из пары.

10 разряд обоих слов пары используется для указания признака лидирующего или последующего слова. А пять старших разрядов у обоих слов должны содержать 11011. Так что для кодирования символа в паре используется только 20 разрядов:


U' = yyyyyyyyyyxxxxxxxxxx
W1 = 110110yyyyyyyyyy
W2 = 110111xxxxxxxxxx

UTF-16, an encoding of ISO 10646 — 2.1 Encoding UTF-16

Никакой валидации utf16 не требует

Насколько я знаю utf-16 поддерживает суррогатные пары (если лидирующее слово содержит 11011 в старших разрядах). При этом последующее слово обязательно должно содержать 110111 в старших разрядах. Если это не так, то последовательность невалидна.

В языках типа C/C++ или Rust используется ручное управление памятью, поэтому программисты тратят больше времени на написание кода, управление временем жизни объектов, а затем на отладку.

Вообще-то в Rust автоматическое управление памятью и за соответствием времен жизни ссылок и объектов следит компилятор.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity