Size: a a a

Rust — русскоговорящее сообществo

2020 April 18

АГ

Алексей Герасимов in Rust — русскоговорящее сообществo
почему нельзя писать так?
struct Foo;
impl Foo {
 fn bar(&mut self, s: String) { }
 fn baz(&mut self) -> String { … }
}

let mut foo = Foo;
foo.bar(foo.baz());

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> src/main.rs:9:13
 |
9 |     foo.bar(foo.baz());
 |     --- --- ^^^ second mutable borrow occurs here
 |     |   |
 |     |   first borrow later used by call
 |     first mutable borrow occurs here

error: aborting due to previous error

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

Э

Эрик in Rust — русскоговорящее сообществo
Ilyas Gasanov
Быть или не быть T: Into<Foo> вместо Foo в аргументе метода — вот в чём у меня вопрос… 🤔
Чтобы можно было пихать объект сразу, а не писать .into() каждый раз как пихаешь.
источник

АГ

Алексей Герасимов in Rust — русскоговорящее сообществo
Ilyas Gasanov
Быть или не быть T: Into<Foo> вместо Foo в аргументе метода — вот в чём у меня вопрос… 🤔
может быть полезно чтобы передавать Some(5) и 5 если ожидается Into<Option<i32>> например, я так только использовал
источник

Э

Эрик in Rust — русскоговорящее сообществo
Алексей Герасимов
почему нельзя писать так?
struct Foo;
impl Foo {
 fn bar(&mut self, s: String) { }
 fn baz(&mut self) -> String { … }
}

let mut foo = Foo;
foo.bar(foo.baz());

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> src/main.rs:9:13
 |
9 |     foo.bar(foo.baz());
 |     --- --- ^^^ second mutable borrow occurs here
 |     |   |
 |     |   first borrow later used by call
 |     first mutable borrow occurs here

error: aborting due to previous error

ведь аргументы раньше вызова функции вычисляются, значит и ссылки должны в том же порядке захватываться
Потому что borrow checker недостаточно умён. Пиши
let a = foo.baz(); foo.bar(a);
источник

АГ

Алексей Герасимов in Rust — русскоговорящее сообществo
Эрик
Потому что borrow checker недостаточно умён. Пиши
let a = foo.baz(); foo.bar(a);
то есть он смотрит не по порядку вычисления а грубо говоря слева направо? как-то странно
источник

IG

Ilyas Gasanov in Rust — русскоговорящее сообществo
Эрик
Чтобы можно было пихать объект сразу, а не писать .into() каждый раз как пихаешь.
Да, но с этим есть риск попасть на trait hell. Например, у TryFrom уже присутствует дженерик реализация для всех типов с From, и из-за неё я не могу объявить в своём коде другую дженерик-реализацию для своих типов, если они даже чисто теоретически могут где-то пересечься с теми типами.
источник

Э

Эрик in Rust — русскоговорящее сообществo
Нет, он смотрит на твою строку, видит, что ты в функцию передаёшь &mut self, &mut self и говорит, что так нельзя.
источник

В

Вафель in Rust — русскоговорящее сообществo
Алексей Герасимов
почему нельзя писать так?
struct Foo;
impl Foo {
 fn bar(&mut self, s: String) { }
 fn baz(&mut self) -> String { … }
}

let mut foo = Foo;
foo.bar(foo.baz());

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> src/main.rs:9:13
 |
9 |     foo.bar(foo.baz());
 |     --- --- ^^^ second mutable borrow occurs here
 |     |   |
 |     |   first borrow later used by call
 |     first mutable borrow occurs here

error: aborting due to previous error

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

{
   let tmp = foo.baz();
   foo.bar(tmp)
}


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

АГ

Алексей Герасимов in Rust — русскоговорящее сообществo
Вафель
потому что твой код эквивалентен

{
   let tmp = foo.baz();
   foo.bar(tmp)
}


Или вроде того. В общем параметры пересекаются с методом по лайфтаймам, известный факт. Единственный способ эого избежать — явно создать отдельную переменную
так у &mut self который передается в baz лайфтайм же меньше должен быть, тлько на время вызова baz, нет?
источник

Э

Эрик in Rust — русскоговорящее сообществo
Вафель
потому что твой код эквивалентен

{
   let tmp = foo.baz();
   foo.bar(tmp)
}


Или вроде того. В общем параметры пересекаются с методом по лайфтаймам, известный факт. Единственный способ эого избежать — явно создать отдельную переменную
foo.bar({
 let mut tmp = foo.baz();
 &mut tmp
});
источник

В

Вафель in Rust — русскоговорящее сообществo
Алексей Герасимов
так у &mut self который передается в baz лайфтайм же меньше должен быть, тлько на время вызова baz, нет?
Ну вот в том-то и дело что в baz лайфтайм оказывается больше, чем ты ожидаешь. Где-то было issue на тему почему так, но что-то не могу найти(
источник

АГ

Алексей Герасимов in Rust — русскоговорящее сообществo
Вафель
Ну вот в том-то и дело что в baz лайфтайм оказывается больше, чем ты ожидаешь. Где-то было issue на тему почему так, но что-то не могу найти(
ясно, игрушечный язык
источник

IG

Ilyas Gasanov in Rust — русскоговорящее сообществo
Ilyas Gasanov
Да, но с этим есть риск попасть на trait hell. Например, у TryFrom уже присутствует дженерик реализация для всех типов с From, и из-за неё я не могу объявить в своём коде другую дженерик-реализацию для своих типов, если они даже чисто теоретически могут где-то пересечься с теми типами.
Ещё такой вопрос. Скажем, у меня есть обёртка вокруг обычного слайса, которая при оборачивании просто валидирует его содержимое по заданным критериям, и возвращает Result. Как её было бы идиоматичнее инстанциировать — через кастомный метод вроде new(), или через реализацию TryFrom?
источник

Э

Эрик in Rust — русскоговорящее сообществo
Ilyas Gasanov
Ещё такой вопрос. Скажем, у меня есть обёртка вокруг обычного слайса, которая при оборачивании просто валидирует его содержимое по заданным критериям, и возвращает Result. Как её было бы идиоматичнее инстанциировать — через кастомный метод вроде new(), или через реализацию TryFrom?
Если Result, то TryFrom.
источник

Э

Эрик in Rust — русскоговорящее сообществo
Но можно написать from_slice() и уже этот from_slice() вызывать для реализации TryFrom.
источник

В

Вафель in Rust — русскоговорящее сообществo
Ilyas Gasanov
Быть или не быть T: Into<Foo> вместо Foo в аргументе метода — вот в чём у меня вопрос… 🤔
Имхо, в общем случае — нет. Но иногда имеет смысл, чтобы сделтаь API более юзабельным ¯\_(ツ)_/¯
источник

IG

Ilyas Gasanov in Rust — русскоговорящее сообществo
У TryFrom минус в том, что референсы на массивы не коэртятся в референсы на слайсы неявно, а дженерик по вышеобозначенной причине я написать не могу. А реализовывать трайфром для массива каждой длины мне просто влом. %)
источник

IG

Ilyas Gasanov in Rust — русскоговорящее сообществo
И получаются костыли вроде Bar::try_from(&[Foo1, Foo2, ...] as &[Foo]). %)
источник

Э

Эрик in Rust — русскоговорящее сообществo
Ilyas Gasanov
У TryFrom минус в том, что референсы на массивы не коэртятся в референсы на слайсы неявно, а дженерик по вышеобозначенной причине я написать не могу. А реализовывать трайфром для массива каждой длины мне просто влом. %)
В смысле? Там же когда передаётся &[1,2,3], он Deref'ом превращается в ссылку на слайс?
источник

IG

Ilyas Gasanov in Rust — русскоговорящее сообществo
Эрик
В смысле? Там же когда передаётся &[1,2,3], он Deref'ом превращается в ссылку на слайс?
В том-то и соль, что в TryFrom из-за дженерика с From в core этот фокус не прокатывает. %)
источник