Pull to refresh
38

User

8
Subscribers
Send message

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

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

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

Еще для снижения количества копипасты есть макросы, даже 2 вида: Macros By Example и процедурные макросы.

Первый тип не сложный, легко пишется, и неплохо помогает, но все же не настолько могущественны как второй тип.

Чтобы макросы второго типа (я просто не уверен, как нормально это "Macro By Example" перевести, чтобы было понятно что это) писать было проще и веселее можно использовать другие макросы, например quote. Вот мой личный пример его использования.

Не надо оператор is использовать для сравнения чисел, это не проверка на равенство, а проверка идентичности (т.е. что объект один и тот же) ((0.1+0.2) == 0.3 тоже выведет False).

Вот пример, почему такое его использование приведет к проблемам:

Заголовок спойлера
x = 100
y = 100
print(x is y) # Output: True
print(id(x))
print(id(y))
x += 100
y += 100
print(x is y) # Output: True
print(id(x))
print(id(y))
x += 100
y += 100
print(x is y) # Output: False
print(id(x))
print(id(y))

Не понятно написал, попробую раскрыть мысль в этом комментарии. Я "подсадил" друзей и родственников на эти программы. Они, как раз, более чем обычные пользователи и 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)
})

Information

Rating
4,349-th
Registered
Activity