Обновить
8
30
Иван Удовин@wilcot

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

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

Не будет работать в фоне. Архитектурно отличия скорее нет, оно есть только в деталях реализации. У меня не получилось с Jami или Tox из-за нестабильности звонков и самого приложения.

Чтобы peer узнал о другом peer, нужен третий участник (в данной статье это ntied-server). Serverless не получится, я даже не знаю, возможно ли здесь достичь serverless, при отсутствии статического белого IP хотя бы у одного участника.

Если участник не в сети, то сообщение попадает в очередь и потом когда-нибудь, когда оба участника в сети, будет доставлено. Здесь нет сервера для таких случаев, я не решал пока такую проблему.

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

сможешь ли ты сделать лучше не полагаясь целиком и полностью на свой маленький релей сервер

Да, в моем случае уже получилось лучше, чем Jami, в плане работоспособности. Если говорить про OpenDHT, то интеграция возможна, но это задача низкого приоритета, поскольку мой текущий подход уже решает основные проблемы.

Стоит ли заморачиваться с п2п

Стоит, ведь P2P подразумевает прямое соединение между собеседниками (в случаях когда это возможно), которое ограничено только пропускной способностью канала собеседников, а не сервера.

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

Есть две проблемы, почему здесь не используется только message_id (который генерируются UUIDv7):

  1. В распределенной системе нет гарантии, что у обоих участников часы синхронизированы.

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

log_id позволяет сериализовать запись каждого сообщения не зависимо от времени, гарантируя что у каждого собеседника история является append-only.

Запустить приложение можно так:
cargo run --bin ntied

За лицензирование отдельное спасибо, обязательно разберусь.

А вы им пользовались? Перед тем как что-то колхозить, были попытки подружиться в том числе и с Jami. К сожалению, с простыми звонками ничего не вышло: у меня и у собеседника были те же проблемы - постоянно надо дергать аудио, чтобы нужное устройство ввода/вывода использовалось, прерывания звонков. Для моих целей Jami как и Tox, не подошел на тот момент.

Когда реализовывал транспорт я смотрел на спецификацию quic чтобы почерпнуть немного идей. Однако руки еще не дошли изучить как обстоят дела с реализацией quic для rust и насколько его реалистично адаптировать к p2p.

Использовалась библиотека sea, но возникли проблемы с линковкой зависимостей под windows. Сейчас там 2 кодека - raw и ADPCM, реализацию можно найти в репозитории.

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

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

В принципе, а почему бы и сейчас не открыть. Точно не фейк, можно проверить

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

но обладает своими уникальными фичами.

О каких уникальных фичах идет речь? Поиск по ключу, вставка, удаление, итерирование есть в обычном map[K]V. Упорядочивание по компаратору в каком-нибуть BTree, RBTree или AVLTree есть.

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

Хотелось бы конкретики. Открыл код, по сравнению с листом - код выглядит сложнее. По сравнению с деревьями поиска +- одинаково, может чуть проще.

Как итог:

  1. Никакого описания принципа работы.

  2. Отсутствие оценки скорости работы "уникальных операций" (хотя бы асимптотическая оценка).

  3. Отсутствие примеров, когда данная структура может дать преимущество над общеизвестными бинарными деревьями поиска и хеш-таблицами.

Пост об утилите на 10 простых строчек, да и еще с ошибками в одном предложении ("простую версия"). Зачем, для кого, почему...

Нужно считать по байтам:
2(порт источника)+2(порт назначения)+4(номер последовательности)+4(номер подтверждения)+1(длина заголовка и часть резерва).

На расте невыразимо

Верно. Хотя можно сделать что-то альтернативное, но это все же не то, что вам надо:

trait Foo {
    fn foo(&self);
}

fn do_smth(v: impl Foo) {
    v.foo();
}

Для enum конечно еще придется попотеть, чтобы интерфейс Foo руками реализовывать не пришлось:

// А еще надо реализовать сам derive(Foo).
#[derive(Foo)]
enum MyEnum {
    A(TypeA), // TypeA реализует Foo
    B(TypeB), // TypeB реализует Foo
}

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

Вообще, на тему достоинств и недостатков различных моделей дженериков (а у Rust и C++ они различаются) есть хорошая статья.

Это уже какой-то антипаттерн.

Возможно, хотя встречается и не сказать что редко.

я уж промолчу что вы свой Option на расте написать не сможете

Точно не смогу? Вроде смог:

enum MyOption<T> {
    Some(T),
    None,
}

enum MyEnum {
    Int(i64),
    NullInt(MyOption<i64>),
}

fn do_smth(v: MyEnum) {
    match v {
        MyEnum::Int(v) => {}
        MyEnum::NullInt(MyOption::Some(v)) => {}
        MyEnum::NullInt(MyOption::None) => {}
    }
}

Вообще, то что вы привели это хардкоженные в компилятор вещи,

Хорошо, удаляем MyEnum::NullInt(OptionI64::None) :

error[E0004]: non-exhaustive patterns: `MyEnum::NullInt(MyOption::None)` not covered
  --> src/main.rs:73:11
   |
73 |     match v {
   |           ^ pattern `MyEnum::NullInt(MyOption::None)` not covered
   |
note: `MyEnum` defined here
  --> src/main.rs:67:6
   |
67 | enum MyEnum {
   |      ^^^^^^
68 |     Int(i64),
69 |     NullInt(MyOption<i64>),
   |     ------- not covered
   = note: the matched value is of type `MyEnum`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
   |
75 ~         MyEnum::NullInt(MyOption::Some(v)) => {},
76 +         MyEnum::NullInt(MyOption::None) => todo!()
   |

Да, опять случай конечно не совсем реальный, но все же с вами не соглашусь. На реальный можно глянуть например сюда и сюда. На более сложных примерах я вообще удивляюсь, как подробно и понятно компилятор объясняет почему я не прав. Да, в редких случаях может выдать что-то не очевидное (как-то с GAT имел дело, та еще мистика, особенно когда там еще с lifetime-ми надо поработать).

и тут даже не нужен визит,

Да, в этом примере может и не нужен, но это уже из-за того что пример построен на стандартных enum-ах.

в visit компилятор заставит обработать все случаи

Интересно увидеть, как будет выглядеть ошибка. Сколько сотен строк с объяснениями как компилятор пытался подставить типы в шаблоны, да ничего не вышло. Вполне возможно что в современном C++20 это выглядит читаемо, поэтому интересно увидеть какой-нибудь такой пример.

Мне интересно, как будет выглядеть на variant+visit такой простой пример:

fn do_smth(v: Result<Option<i64>, String>) {
    match v {
        Ok(Some(v)) => {}
        Ok(None) => {}
        Err(v) => {}
    }
}

А еще интересно, как будет выглядеть вывод компилятора, когда я забуду указать один из случаев.

Например как будет выглядеть ошибка, если я удалю ветку с Err(v) => {}:

error[E0004]: non-exhaustive patterns: `Err(_)` not covered
   --> main.rs:12:11
    |
12  |     match v {
    |           ^ pattern `Err(_)` not covered
    |
note: `Result<Option<i64>, std::string::String>` defined here
   --> /home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:502:1
    |
502 | pub enum Result<T, E> {
    | ^^^^^^^^^^^^^^^^^^^^^
...
511 |     Err(#[stable(feature = "rust1", since = "1.0.0")] E),
    |     --- not covered
    = note: the matched value is of type `Result<Option<i64>, std::string::String>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
14  ~         Ok(None) => {},
15  ~         Err(_) => todo!()
    |

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

Информация

В рейтинге
280-й
Откуда
Могилев, Могилевская обл., Беларусь
Дата рождения
Зарегистрирован
Активность

Специализация

Бэкенд разработчик