Обновить
44
0.1

Типострадалец

Отправить сообщение

В случае с встраиваемым ассемблером и, возможно, атрибутом no_mangle, нельзя получить unsound код, что бы ты ни делал.

Можно, и очень легко. Ассемблер позволяет написать инструкцию, для которой поведение процессора буквально может быть не определено, а также написать код, который ломает правила языка. Через no_mangle же можно сделать свою функцию, которая пересечётся по имени с функцией из зависимости из нативного кода, перекроет оригинальное определение с абсолютно другим ABI и разломает всю программу, если дотуда дойдёт исполнение.

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

Потому что код так написан. Если нужно просто на печать выводить, то владение строкой можно и не принимать, а принимать лишь ссылку. Тогда можно будет и вызвать два раза подряд.

Про null это вообще бред, вот у нас есть некий класс который может быть нулл. В расте для такого случая предполагается сделать указатель на этот класс опциональным и проверять на None. Но если всё равно проверять на None в чём проблема блин проверить на null? Это буквально тот же самый if !class.is_none только if class != null. Изобрели костыль и ходят радостные.

Фишка в том, что в Rust можно не делать Option и тогда нигде на отсутствующее значение проверять не надо. В других языках, где null есть, обычно он является допустимым значением для всех ссылочных типов, а записать тип, который null в число допустимых значений не включает, нельзя.

А можете привести конкретные детали синтаксиса, которые вызывают затруднение?

Авторы языка не осилили написать проект, ради которого язык и создавался

Уточнение: проект начали писать задолго до релиза 1.0, когда в Rust ещё был сборщик мусора и зелёные потоки, которые можно было смешивать с нативными. И да, CSS-движок Stylo оттуда в итоге в Firefox перенесли.

Асинхронность в расте — несомненный костыль, который рассыпается (и будет рассыпаться всегда, by design) в кластере.

[citation needed]

Зачем нужно было публиковать статью про свою библиотеку во второй раз, не добавив при этом ничего полезного?

А зачем вы async-trait использовали? Асинхронные функции в трейтах уже два года как поддерживаются нативно

Для пытливых умов есть замечательный материал, модель Stacked Borrows. Это экспериментальная формализация, которая пытается строго определить, какие комбинации заимствований допустимы в Rust.

Уже два года как есть ещё и Tree borrows, более разрешительная модель, которая имеет больше шансов стать окончательной.

а в чем оно расходится? Duck typing подсказывает, что это и есть интерфейсы.

Трейты позволяют описывать ассоциированные типы. Пример - Iterator из стандартной библиотеки:

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // другие методы
}

У трейтов могут быть методы с реализацией по умолчанию. Пример - трейт Iterator выше: у него 76 методов, но для реализации достаточно лишь одного: next. Все остальные реализованы в терминах next.

Трейты позволяют ссылаться на тип, который его реализовывает, через Self. Пример - PartialEq:

trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    fn eq(&self, other: &Rhs) -> bool;
}

В ООП-языках аналогичные интерфейсы или принимают верхний объект из иерархии типов (Java), или вынуждены принимать сравниваемый тип обобщённым параметром (C#) и заставляют повторять тип в описании ограничений.

Трейты могут быть реализованы не безусловно, а только при соблюдении каких-то условий. Например, трейт Serialize из популярной библиотеки serde. Он реализован для Vec только в том случае, если Serialize реализован для его элементов:

impl<T> Serialize for Vec<T>
where
    T: Serialize,
{
    // ...
}

Автору типа не обязательно знать о трейте для его реализации. Пример выше: Vec определён в стандартной библиотеке, но трейт Serialize и его реализация для Vec определены в serde.

Трейты могут быть реализованы для многих типов сразу. Пример: в стандартной библиотеке есть два трейта для конвертации между типами: From и Into. Реализация первого автоматически даёт реализацию второго:

impl<T, U> Into<U> for T
where
    U: From<T>,
{
    // ...
}

Как видите, отличий предостаточно.

Используйте языки с RAII

Представьте, что у вас enum имеет один из элементов MyEnum::Variant10(u8, u8, u8), а все остальные MyEnum::Variant0-9(u8), память будет выделена так:

  1. дискриминант (по умолчанию usize т.е. 4 или 8 байт)

  2. 3 байта Итого: 11 байт на adm64

Зачем вы дезинформируете людей? Во-первых, по умолчанию размер дискриминанта выбирается минимального размера, подходящего для хранения всех вариантов. Во-вторых, если бы бы удосужились действительно проверить этот код, то увидели бы, что он печатает 4:

enum A {
    V0(u8, u8, u8),
    V1(u8, u8, u8),
    V2(u8, u8, u8),
    V3(u8, u8, u8),
    V4(u8, u8, u8),
    V5(u8, u8, u8),
    V6(u8, u8, u8),
    V7(u8, u8, u8),
    V8(u8, u8, u8),
    V9(u8, u8, u8),
    V10(u8),
}

fn main() {
    println!("{}", std::mem::size_of::<A>());
}

Что обидно, поскольку конкретно приведённый фрагмент прям напрашивается на переписывание на сумм-типы

То есть этот unsafe опять даже ноду в трех двусвязных списках держать не позволит?

А, простите, зачем?

Что поливается говном, если никакой реализации профилей безопасности пока не существует?

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

И вам бы это было понятно, если бы вы таки удосужились прочитать текст по ссылке.

Шон и его фанаты как раз таки поливают профили говном при каждой возможности.

И правильно делают, потому что профили в том виде, в котором их продвигают, не работают и принципиально работать не могут.

Вы про то, что надо знать детали реализации slice, чтобы им пользоваться? В остальном вроде всё терпимо.

Ох, если бы только это.

nil

append в nil-слайс возвращает слайс с единственным значением, но попытка вставки в nil-мапу паникует. Функцию cap можно вызвать на слайсе, но не на мапе.

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

Имена переменных могут затенять встроенные идентификаторы:

package main

import "fmt"

func main() {
	fmt.Println(true) // true
	true := 42
	fmt.Println(true) // 42
}

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

У каждого типа есть нулевое значение. При создании структуры можно опустить некоторые из полей, в том числе все. Неуказанные поля получают нулевые значения. Добавил новое поле? Ищи все места самостоятельно. И не забудь, что, если структура не приватная, то внешний код тоже может такую ерунду делать.

У интерфейсов тоже есть нулевое значение, nil, и это отличается от интерфейса, который хранит nil-значение:

package main

import "fmt"

type I interface {
	f()
}

type C struct{}

func (c *C) f() {}

func main() {
	var in I
	fmt.Println(in == nil) // true
	var c *C = nil
	in = c
	fmt.Println(in == nil) // false
}

Переменные цикла переиспользуются между итерациями, что в своё время привело к отзыву трёх миллионов сертификатов Let's Encrypt. Сейчас это поправили, но более разумное поведение, ЕМНИП, всё ещё opt-in.

Каналы могут быть в двух невалидных состояниях: nil и закрытом. Посылка и получение в nil-канал блокируются навечно, в то время как отправка в закрытый канал паникует, а получение из закрытого канала возвращает нулевое значение. Зачем разница в поведении - неясно.

defer вызывается в конце функции. Функции, а не лексической области видимости.

Одно ключевое слово for имеет 11 разных значений в зависимости от семантики. У четырёх из них поведение зависит от того, по какому типу итерация, но синтаксис при этом идентичный. А, и ещё можно итерироваться с одной переменной по слайсу и получить индексы вместо значений.

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

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

По моему, как-то многовато надо в голове держать для так называемого простого языка.

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

Только [[maybe_unused]] ещё докинуть, чтобы компилятор на определение гуарда не ругался.

А этот антипаттерн от языка не зависит

В общем, property-based testing — это эксперимент, который надо уметь ставить, и его результаты тоже надо уметь обрабатывать. Об этом прекрасно рассказал автор QuickCheck в видео John Hughes — Building on developers' intuitions (...) | Lambda Days 19.

Понимаю, что статья давняя, но можете вставить ссылку на искомое видео, чтобы читателям не пришлось искать самостоятельно?

1
23 ...

Информация

В рейтинге
3 484-й
Зарегистрирован
Активность