Привет всем! Вопрос по идеологии раста. Есть некоторая обертка над крейтом

rusqlite. Структурка Connection позволяет исполнять запросы через немутабельный интерфейс:

fn execute(&self, ...) // Тут ссылка немутабельная

Но ведь в сущности состояние базы меняется.
В обертке у меня методы высокоуровневые (add_something, update_something, update_stuff, ...). Некоторые из них требуют начала транзакций, которые уже требуют мутабельную ссылку на Connection.

И вот получается, что какие-то методы обертки принимают &self, а какие-то -- &mut self. В общем криво.

Ясно, что можно заюзать RefCell, это будет работать, но я задумался... А вот какую семантику раст вкладывает в mut?

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

То есть:

fn update_something(&mut self, ...) {
// Тут ссылка на мутабельный
// коннекшн не требуется!
self.connection.execute(...);
}

fn update_stuff(&mut self, ...) {
// А тут нужна!
let tx = self.connection
.transaction();
...
}

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

20 ответов

77 просмотров

update_stuff требует мутабельную ссылку из-за внутренней реализации метода transaction у коннекшна По «идеологии» раста не подскажу, но вы, мне кажется, не о том состоянии говорите Состояние коннекшна меняется, а не базы, но по сути оба апдейта изменяют состояние базы, но с состоянием коннекшна работают по разному

Georgy-Firsov Автор вопроса
Alexey
update_stuff требует мутабельную ссылку из-за внут...

Да, это в принципе понятно, почему где-то нужна мутабельная ссылка на коннекшн, а где-то -- нет. Это мутабельность языковая. А вот с точки зрения бизнес-логики и там, и там меняется база. Это логическая мутабельность получается. Обертка над базой отвечает за бизнес-логику. И вот правильно ли в решениях о "константности" методов этой обертки отталкиваться от бизнес-логики, а не от конкретной реализации внутренностей? В обычном случае так бы и поступил, но раст много где вкладывает свою идеологию. Останется ли верным и это правило?

Georgy Firsov
Да, это в принципе понятно, почему где-то нужна му...

Так опять же - &mut self это не база, а коннекшн к базе Как это вам бизнес-логику ломает

Georgy-Firsov Автор вопроса
Alexey
Так опять же - &mut self это не база, а коннекшн к...

Это ломает красоту и консистентность, лишние вопросы вызывает: fn update_something() { let engine = build_engine(); engine.update_something(); } fn update_stuff() { let mut engine = build_engine(); engine.update_stuff() } Вот как бы два однородных метода используются, но сразу же у меня возникает вопрос: ну а чего один из них требует мутабельности, а второй -- нет? И это надо лезть в реализацию, смотреть, как сделано и пр. Это если не расстрельная статья, то немалый срок точно. Вызывать у читателя лишнии вопросы -- нехорошо. Вот и вопрос: правильно сделать везде &mut self в update_* или использовать RefCell?

Georgy Firsov
Это ломает красоту и консистентность, лишние вопро...

Вы так паритесь по поводу красоты, тогда уж задумайтесь и про название методов Почему (если мутабельности не избежать) вам в названии самого метода не обозначить что это апдейт, но не такой апдейт как другой Это, выражаясь вашими формулировками, тоже статья - так методы называть

Georgy Firsov
Это ломает красоту и консистентность, лишние вопро...

странный пример update без mut? это как... (учитывая что в функцию ничего не передается)

Georgy-Firsov Автор вопроса
Georgy-Firsov Автор вопроса
Alexey
Вы так паритесь по поводу красоты, тогда уж задума...

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

Georgy Firsov
С точки зрения базнес-логики это как раз два одина...

так если они оба одинаковы, то как один из них - мутабельный, а другой нет?

Georgy-Firsov Автор вопроса
К|/|pи/\/\ 6е3yглbIи
так если они оба одинаковы, то как один из них - м...

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

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

К|/|pи/\/\ 6е3yглbIи
это получается "простые" транзакции с бд делаются ...

сорри, я могу просто не понимать сам логики, то ли я туповат, то ли информации мало)) прост помочь пытаюсь)

Georgy-Firsov Автор вопроса
К|/|pи/\/\ 6е3yглbIи
это получается "простые" транзакции с бд делаются ...

Ладно, подробнее: trait Storage { fn update_something(&self, args: Args) -> Result<()>; fn update_other(&mut self, args: Args) -> Result<()>; } struct DbStorage { db: rusqlite::Connection, } impl Storage for DbStorage { fn update_something(&self, args: Args) -> Result<()> { self.db .execute("...", args)?; } fn update_other(&mut self, args: Args) -> Result<()> { let tx = self.db .transaction()?; tx.execute("...", args)?; tx.execute("...", args)?; tx.commit(); Ok(()) } } struct<S: Storage> Engine { storage: S, } impl Engine { fn update_something(&self, args: RawArgs) -> Result<()> { let args = convert(args); self.storage .update_something(args); } fn update_stuff(&mut self, args: RawArgs) -> Result<()> { let args = convert(args); self.storage .update_stuff(args); } } Engine содержит ещё ряд полей, но в данном контексте это неважно. Суть в следующем: - хранилище можно менять, реализация может меняться (требования к мутабельности storage, соответственно, -- тоже) - используется в клиентском коде сугубо Engine. С точки зрения пользователя разницы в обновлении something и stuff быть не должно. Отсюда не должно быть различий между интерфейсами вызовов update_* функций (ну какая разница, юзается там внутри транзакция или нет). Соответственно они все либо &self, либо &mut self. А вот как идеологически верно то? Вот в трейте что правильно будет из этого написать? Если отталкиваться от бизнес-логики, то это &mut self, и везде, где обновление идет, использовать let mut engine. Если же так не принято, и mut это плохая практика в данном случае, то внутри DbStorage надо будет db завернуть в RefCell. А снаружи везде будет &self и let engine. Так тоже работает.

Georgy Firsov
Ладно, подробнее: trait Storage { fn update_som...

"Even though we don’t mutate the connection, we take a &mut Connection so as to prevent nested transactions on the same connection. For cases where this is unacceptable, Transaction::new_unchecked is available." отсюда

так что я думаю ты прав, приходится залазить в документацию и читать почему так это не проблема раста, а крейта им стоило бы, возможно, сделать не new() для транзакций, а new_checked() и переименовать метод в transaction_checked(), но я не знаю насколько это сходится с "семантикой" самого sqlite он же специально сделан под "простые задачи", поэтому и тут тоже таким принципом руководствовались) ну и документация в расте очень приятная как для глаза, так и для обработки информации, так что это немного заняло по времени найти это

К|/|pи/\/\ 6е3yглbIи
так что я думаю ты прав, приходится залазить в док...

method и method_unchecked это в целом стандартный нейминг

Пух
method и method_unchecked это в целом стандартный ...

да, согласен в std такого нет, там checked и unchecked везде

Georgy-Firsov Автор вопроса
К|/|pи/\/\ 6е3yглbIи
"Even though we don’t mutate the connection, we ta...

О, вот это я пролистал мимо Спасибо) Вот теперь всё сходится

К|/|pи/\/\ 6е3yглbIи
так что я думаю ты прав, приходится залазить в док...

Ну да, ключевое — сначала трахаемся, а только потом читаем доку 😂

Похожие вопросы

Обсуждают сегодня

Всем привет. Ребята, подскажите, пожалуйста. у ботов есть ограничение на отправку сообщений - 30 сообщений в секунду, эти ограничения накладываются на все сообщения? или на со...
Artem Stormageddon
2
Ребята, всем привет. Подскажите, пожалуйста, можно ли как-то через бота понять, что этого бота добавили в группу\канал и выдали ему права администратора?
Artem Stormageddon
9
Привет всем! Почему этот код не срабатывает при добавлении или удалении пользователя из чата? bot.on('chat_member', async (ctx) => { console.log(ctx); }) bot.launch({allo...
Alexander
7
Всем привет. Не понимаю, в чём тут шутка юмора. Убирается только разрешение на send_messages. А send_media_messages остаётся. Как сделать, чтобы оба убирались? await b...
Alexander
2
Есть тут кто занимается разработкой серваков майна? Или знакомые
meow *
3
'frakturBold' => ['𝖆', '𝖇', '𝖈', '𝖉', '𝖊', '𝖋', '𝖌', '𝖍', '𝖎', '𝖏', '𝖐', '𝖑', '𝖒', '𝖓', '𝖔', '𝖕', '𝖖', '𝖗', '𝖘', '𝖙', '𝖚', '𝖛', '𝖜', '𝖝', '𝖞', '𝖟', '𝕬', '𝕭', '𝕮', '𝕯'...
Roma
4
Есть ли лимиты на кол-во вебхук по домену? Стоит в данный момент 900+ ботов и бывает бот перестает отвечать (не приходят вебхуки) 🐒 Помогает только перезапуск
ᅠ [ Кому не ответил, дублируйте ]
11
Товарищи, здравствуйте Подскажите, пожалуйста, может кто-нибудь сталкивался с такой задачей Через вебапку можно сканировать qr-код, а есть ли возможность считывать nfc?
Artem Stormageddon
8
а что делать если тебя убивают на картах?
Yarik yarik kyda ti lezesh
43
Визуальное отображение моделей таблиц sql какое посоветуете?
Shen
7
Карта сайта