Обновить
36
0

Пользователь

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

Не понятно написал, попробую раскрыть мысль в этом комментарии. Я "подсадил" друзей и родственников на эти программы. Они, как раз, более чем обычные пользователи и GUI могут пользоваться yt-dlp без проблем. Но часто ютуб ломает своё все, из-за чего ломается yt-dlp и мне приходится этим людям объяснять:

  • как и что нужно скачать с гитхаба;

  • как найти расположение файла по ярлыку;

  • куда же этот новый yt-dlp нужно положить

  • и т.д.

То есть люди как раз самые что ни на есть обычные и кроме этого у них особых проблем после начальной настройки нет.

Для yt-dlp можно без проблем найти отдельные GUI, вроде Open Video Downloader или Seal. Объяснять, как обновить yt-dlp после очередного обновления ютуба сложно, но вполне возможно.

вы серьёзно считаете такое неадекватное поведение компилятора приемлемым

Это не автор считает, это стандарт так считает.

Оракл в свое время начал выпуск своей СУБД с версии 2, мотивируя тем, что никто не хочет пользоваться первой версией

Так в этом и проблема. Сам по себе номер версии вообще ничего не значит. Изменение минорной версии не гарантирует, что у кода не изменилось API (хотя для этого есть помощники). Можно тогда просто на версии с датами перейти (вроде 2024.04.1), код надежнее это не сделает, зато выглядит солидно.

С 0.X.Y версиями ситуация получается странная - даже если библиотека уже готова для использования в продакшен среде, то переход на 1.0.0 создаст много проблем с не очевидными выгодами. Менять мажорную версию без нарушений обратной совместимости странно, т.к. cargo автоматически не обновит версию с 0.X.Y до 1.0.0, что заставляет всех пользователей библиотеки делать это вручную. А из плюсов только то, что версия 1.0.0+ выглядит солиднее.

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

То есть, вы считаете, что эта статья о сравнение Rust и C (даже проигнорируем тот факт, что я так не считаю, просто часто это то, как люди воспринимают Rust. С Python и Golang сравнений больше), подменяете его своим аргументом "в контексте сравнения с C++, потому что считаю сравнение Rust и Си бессмысленным и просто неправильным" и пытаетесь разбить уже свой собственный аргумент? Да это же соломенное чучело.

Да по вашим комментариям можно целый курс по черри-пикингу сделать. Я даже название придумал - "Краткий курс черри-пикинга или как проигнорировать контекст дискуссии так, чтобы выставить своего оппонента максимальным идиотом".

Если вы забыли (или не знали) контекста и этой дискуссии, то давайте я вам (и всем, кто будет читать это после) расскажу:

  1. у меня в статье есть пример кода, который использует потоки:

    Заголовок спойлера
    let rows = img.rows_mut().collect::<Vec<_>>();
    std::thread::scope(|scope| {
        ...
        for (y, chunk) in rows.into_iter().enumerate() {
            scope.spawn(move || {
                ...
                for (x, pixel) in chunk.enumerate() {
                    let mut color = Color::default();
                    for _ in 0..samples_per_pixel {
                        ...
                    }
                    *pixel = color.as_rgb(samples_per_pixel);
                }
                ...
            });
        }
    });
    
    img.save("image.png").unwrap();
    

    Там же указаны гарантии языка для этого кода:

    Заголовок спойлера
    • в этом коде нет гонок данных;

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

    • невозможно написать эту программу так, чтобы в момент img.save хоть один из потоков был бы еще жив.

  2. feelamee на это пишет, что "На плюсах это пишется не сложнее" и "Думаю тут не осталось сомнений, что в таком простом варианте, у плюсов и раста примерно одинаковые удобства и безопасность". Если "В плюсах сделаете { } просто и в конце jthread сделает join()." с таким кодом:

    Заголовок спойлера
    std::ranges::for_each(std::execution::par,
                      rows | std::views::enumerate,
                      [](auto const i, auto const& el){
                        // logic  
                      });
    
  3. На это я отвечаю " А что будет, если не "сделать { }"? И что будет, если не "в конце jthread сделает join()"? Потому, что в Rust это нельзя не сделать, код не скомпилируется". Потому, что я точно знаю, что если не сделать join() для обычного потока, то это чтение и запись без синхронизации, т.е. это сразу UB и сделать join() автоматически при выходе из скоупа это, насколько я знаю, плохая идея.

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

Вы бы определились уже со своей позицией, а то я только в комментариях к этой статье видел:

Это Rust сообщество токсичное и фанатичное, конечно.

Особенно это смешно, если проследить за последовательностью событий:

Я правильно понимаю, что этот код настолько же безопасен, как "безопасен" и unsafe Rust?

Но почему то в одном надо рассматривать ситуацию, когда какой-то недотепа допускает глупую ошибку, но в другом можно на это не обращать внимание, ведь там есть слово, которое его ТОЧНО остановит

А вы попробуйте привести пример, когда человек случайно не просто unsafe блок напишет, но еще и случайно напишет такой код, который UB вызовет (спойлер: unsafe блок не выключает никакие проверки, даже если его случайно написать, то ничего не изменится).

Если слайсы слишком сложно
Go -- язык простой
позволяет быть продуктивным с минимальным знанием языка

Вам бревно в глазу не мешает?

Так что, что такое "true" в любом случае будет видно сразу.

Только что проверил, golangci-lint run игнорирует такой код (golangci-lint has version 1.55.2):

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	true := rand.Intn(10) > 5
	if true {
		fmt.Println("randomly true")
	}
}

Видимо потому, что true - слишком незначительная часть языка для того, чтобы true было ключевым словом.

Это как view таблицы в базе данных.

Это замечательно, только вот как мне это поможет избежать (это хотя бы можно s[:3:3] починить):

https://go.dev/play/p/7dkrDMD4v9D

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    s = s[:3] // limiting len
    printSlice(s)
}

func printSlice(s []int) {
    s = s[0:cap(s)] // random func ignores limit
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

Или такой гайзенбаг, когда то, упадет код или тихо будет работать неправильно зависит от того, был ли слайс переалоцирован или нет:

https://go.dev/play/p/Fud-Dmdgq1u

package main

import (
	"fmt"
	"math/rand/v2"
)

type SlidingWindow struct {
	s      []int
	offset int
	size   int
}

func (w *SlidingWindow) next() []int {
	if w.offset > (len(w.s) - w.size + 1) {
		return nil
	}
	t := w.s[w.offset : w.offset+w.size]
	w.offset += 1
	return t
}

func sliding_window(s []int, offset int, size int) []int {
	return s[offset : offset+size]
}

func main() {
	s2 := []int{1, 2, 3, 4, 5, 6}
	if rand.IntN(10) > 5 {
		s2 = append(s2, 7) // panics without append
	}

	sw := SlidingWindow{s2, 0, 3}

	for w := sw.next(); w != nil; w = sw.next() {
		fmt.Println(w)
	}
}

то можно рассмотреть и unsafe раст

А можно и не рассмотреть. Unsafe не появится если забыть что-то сделать.

"variant это match из rust для бедных"

Не знаю, как у вас получилось, но вы проигнорировали самое важное предложение в середине комментария:

The match statement here will ensure, at compile-time, that I correctly enumerate all 4 possible combinations

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

В итоге факт 1, 2 спорны, потому что это исследование

Просто замечательно. Я даже не знаю, как это прокомментировать.

ваша статья во многом сравнивает Rust с Си (или зачем такое название)

Ответ находится во втором абзаце моей статьи.

Вы ведь сами попросили примеры

Примеры "половины фактов, которые не имеет смысла, а остальная половина спорна", а не сравнения Rust с C++.

Поэтому я предложил сравнивать с C++, а в таком виде большинство из них уже не являются "преимущестом Rust"

Меня вы в этом не убедили.

тогда докажи, что добавленные строки это НЕ сортировка и проверка уникальности элементов с выдачей ошибки, если они ен уникальные

Может мне еще заодно доказать, что он ракеты на Марс не запускает?

За минуту можно найти, что "это НЕ сортировка и проверка уникальности элементов с выдачей ошибки, если они ен уникальные":

/// This checks every index against each other, and against `len`.
///
/// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..`
/// comparison operations.
fn get_many_check_valid<const N: usize>(indices: &[usize; N], len: usize) -> bool {
    // NB: The optimizer should inline the loops into a sequence
    // of instructions without additional branching.
    let mut valid = true;
    for (i, &idx) in indices.iter().enumerate() {
        valid &= idx < len;
        for &idx2 in &indices[..i] {
            valid &= idx != idx2;
        }
    }
    valid
}

а в чём они в бинарном коде ещё могут измеряться?

Это шутка такая? Может быть все же надо смотреть на то, какие операции происходят? А то 1 стока блокирующего сискола, оказывается, выполняется столько же, сколько и мув из регистра в регистр. Вот чудеса!

я показывааю что оно тут есть

Так покажите, где в релиз версии get_many_check_valid "создание массив, сортировка и проверка на наличие повторений". Вот конкретные строки. И при этом я пытаюсь "с темы съехать"?

Тогда получается 146 строк асм против 97 когда неизвестный индекс.

"создание массив, сортировка и проверка на наличие повторений", видимо, в количестве строк асма выражается, ясно, понятно. Не помните уже, с чего разговор то начался? Могу напомнить, вы в одном комментарии умудрились сделать 4 утверждения, все из который ложны (включая "создание массив, сортировка и проверка на наличие повторений" для не дебаг версии).

И без тыканий, пожалуйста, мы с вами брудершафт не пили.

То ничего не изменится:

Заголовок спойлера
playground::swap: # @playground::swap
# %bb.0:
	mov	eax, dword ptr [rdi]
	mov	ecx, dword ptr [rsi]
	mov	dword ptr [rdi], ecx
	mov	dword ptr [rsi], eax
	ret
                                        # -- End function

playground::main: # @playground::main
# %bb.0:
	push	rbx
	sub	rsp, 80
	mov	dword ptr [rsp + 16], 111
	lea	rax, [rsp + 16]
	#APP
	#NO_APP
	mov	eax, dword ptr [rsp + 16]
	mov	dword ptr [rsp + 16], 222
	lea	rcx, [rsp + 16]
	#APP
	#NO_APP
	mov	ecx, dword ptr [rsp + 16]
	mov	dword ptr [rsp + 16], 333
	lea	rdx, [rsp + 16]
	#APP
	#NO_APP
	mov	edx, dword ptr [rsp + 16]
	mov	dword ptr [rsp + 4], eax
	mov	dword ptr [rsp + 8], ecx
	lea	rsi, [rsp + 12]
	mov	dword ptr [rsp + 12], edx
	lea	rbx, [rsp + 4]
	#APP
	#NO_APP
	mov	rdi, rbx
	call	playground::swap
	#APP
	#NO_APP
	mov	qword ptr [rsp + 64], rbx
	lea	rax, [rip + core::array::<impl core::fmt::Debug for [T; N]>::fmt]
	mov	qword ptr [rsp + 72], rax
	lea	rax, [rip + .L__unnamed_3]
	mov	qword ptr [rsp + 16], rax
	mov	qword ptr [rsp + 24], 2
	mov	qword ptr [rsp + 48], 0
	lea	rax, [rsp + 64]
	mov	qword ptr [rsp + 32], rax
	mov	qword ptr [rsp + 40], 1
	lea	rdi, [rsp + 16]
	call	qword ptr [rip + std::io::stdio::_print@GOTPCREL]
	add	rsp, 80
	pop	rbx
	ret

Такой код не скомпилируется. to_string возвращает строку, а замыкание в filter_map_ok должно возвращать Option. Но можно сделать еще лучше:

Заголовок спойлера
.filter_map_ok(|file_name| {
    let f = file_name
        .file_name()?
        .to_str()?
        .strip_prefix("appmanifest_")?
        .strip_suffix(".acf")?
        .to_string();
    Some(f)
})
  1. Параллельный код. Это (без иронии) замечательно, что в C++ можно написать так же удобно.

    1. А вот про безопасность давайте поговорим отдельно. "В плюсах сделаете { } просто и в конце jthread сделает join()". А что будет, если не "сделать { }"? И что будет, если не "конце jthread сделает join()"? Потому, что в Rust это нельзя не сделать, код не скомпилируется. В обычные потоки (не scoped) нельзя передать не 'static объект (объект, который живет всю жизнь программы). И не в смысле пока жив main, но и после этого;

    2. Что же из этой части в моей статье "не имеет смысла", а что "спорно"?

  2. Обработка ошибок через монады

    1. "Обработка ошибок через монады есть и в плюсах" И надо поддерживать как обработку ошибок монадами, так и исключениями одновременно;

    2. "Какие проблемы у монад я написал в первом комментарии" Да не то, чтобы написали, кроме "Это ужасный код ... все равно смотрится как каша". Дело ваше, чувство прекрасного у всех свое. Меня же больше интересует практическая сторона вопроса, чем эстетическая;

    3. Что же из этой части в моей статье "не имеет смысла", а что "спорно"?

  3. Рефакторинг

    1. "система типов в плюсах не слабее, чем в расте. Думаю с этим спорить бессмысленно". Не готов спорить, у меня нет достаточных компетенций в C++;

    2. "Перечисления в плюсах это std::variant, паттерн матчинг это std::visit". То же самое, не готов спорить, но первый комментарий из первой ссылки из гугла, которую я открыл, с этим не согласен;

    3. Что же из этой части в моей статье "не имеет смысла", а что "спорно"?

  4. Факты

    1. "Слишком мало контекста". Ответы на все эти вопросы вы могли бы получить просто посмотрев выступление, из которого эти тезисы;

    2. "Rust навязывает и заставляет писать правильный код, в отличии от большинства других языков". Ужас то какой, заставляет писать правильный код. Как жить то после такого;

    3. Что же из этой части в моей статье "не имеет смысла", а что "спорно"? Где хотя бы противоречие или несогласие, раз уж вы решили написать почти полторы тысячи символов по этому поводу?

  5. Время компиляции

    1. Видимо хоть в чем-то мы согласны, это уже неплохо;

    2. Что же из этой части в моей статье "не имеет смысла", а что "спорно"?

  6. Сложность

    1. "Rust сложным, насколько я понял по множеству статей, кажется людям, которые переходят с языков вроде python. И причины тут очевидны" Причины настолько очевидны, что их и указывать не надо? И, кстати, будем знакомы, я как раз начал изучать Rust после Python. И я считаю, что Rust - простой;

    2. "Насчет Си - наверно под "маленький" имеются ввиду концепции в языке". Это ему не помогает, в C приходится думать о слишком большом количестве вещей и из-за этого никто не способен писать безопасный код на C и/или C++;

    3. "Option<NotNull<Node<T>>>". Насколько я знаю, в стандартной билбиотеке плюсов код гораздо более нечитаем, тут же просто по переводу можно довольно точно понять, что происходит;

    4. "если там всегда есть объект". Потому, что не всегда там есть объект;

    5. "А если он есть не всегда, тогда зачем NotNull?". Чтобы нельзя было разыменовать нулевой указатель;

    6. "А что вообще значит ситуация, когда Option == None". Что объекта там нет;

    7. "Я под обработкой ошибок понимаю считай весь код, который есть в вашей версии, но нет в моей". Допустим, мне вообще не кажется это важным;

    8. "Поэтому я и сказал, что мне не нравятся исключения в C++". Тогда я не понимаю примера в вашем первом комментарии. Вы не хотите писать "ужасный код" для обработки ошибок как значений и вам не нравятся исключения. Вы хотите жить в мире, где ошибок просто не существует? Я тоже был бы не против, только пока такого не предвидится;

    9. "Наверняка и ваш код на Rust можно сделать более красивым". В данном случае можно т.к. filter_map_ok принимает замыкание, которое возвращает Option. Получилось вот так:

      Заголовок спойлера
      .filter_map_ok(|file_name| {
          file_name
              .file_name()?
              .to_str()?
              .strip_prefix("appmanifest_")?
              .strip_suffix(".acf")
              .map(|app_id| app_id.to_string())
      })
      

      Мне по большому счету все равно. Код с and_then лучше тем, что ему не надо быть в функции, которая возвращает Option. Может быть когда стабилизируют try_blocks так будет удобнее во всех случаях. Для меня большой разницы нет, Option все равно придется как-то проверить и никакая магия не поможет избавиться от последнего map.

В итоге, вы написали очень много слов про C++. У меня в статье C++ упоминается 7 раз. 5 из них это цитаты (или перефразирование цитат) и еще 2 во вступлении. Так что я не очень понимаю, к чему эта гора текста про C++, про который я в статье не говорю? Кто ж виноват в том, что в статьях, из которых я брал тезисы, очень много сравнений с C++.

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

Простите, но я не понял, что вы хотели показать в примере с вектором, там же написано следующее:

  • push_back, emplace_back - If the vector changed capacity, all of them;

  • insert, emplace - If the vector changed capacity, all of them;

  • resize - If the vector changed capacity, all of them.

Полагаться на то, что у ветора при добавлении не изменится capacity - это дело рискованное.

В связном списке Rust точно так же не инвалидирует итератор.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность