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

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 ответов

31 просмотр

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и
так что я думаю ты прав, приходится залазить в док...

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

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

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

Ребята, я за проф советом😅 По микросервисам. В монолите есть общие файлы для сервисов: фетчи, конфиги, либы, утилсы.. как при распиле правильно их поддерживать? Пока вариант д...
Александр Тарасюк
1
а что делать если тебя убивают на картах?
Yarik yarik kyda ti lezesh
43
Подскажите где можно прочитать про реализацию возможности писать человеку при подписке на телеграм канал от имени бота? Было бы не плохо если для Telegraf@3.38.0
Pan Lipton
10
Мне вот что интересно, кто на рфе стартовал/играл, что вы фармили, в каком виде контента он прямо хорош? Экспедиция? Вроде прямо на замазанных мапах рф сдувается
Владислав
20
‌/r/pathofexile moderation changes top scoring links : pathofexile (RSS) Hi, everyone. On behalf of the subreddit mod team, I’m here to give you a few updates on the subreddi...
Esionru
3
Кто нибудь поясните это всё таки вброс или да? Про санктум слышал на поедб вбросили, а по дурке откуда инфа и на сколько это вообще правда? Пахнет шизофренией какой-то ✅Divi...
Dmitry Ritter
9
У вас бывает ощущение, что хочется потратить весь отпуск на то, чтоб только спать?
Николай
15
Как можно настроить фильтр в пое под себя?
Yarik yarik kyda ti lezesh
15
У меня вопрос к знающими, стоит ли вступать в гильдии в игре или лучше полная свобода?
Енот Полоскун
17
Ребят, есть какие нибудь мили билды, способные в шмотках с пола закрывать атлас?
Ninja Obormot
12
Карта сайта