Здесь вы имеете дело с двумя различными типами замыканий - FnOnce
и FnMut
.Оба типа замыканий имеют разные соглашения о вызовах.
Если вы определите свое замыкание как
let mut x = 32;
let g = move || {
x = 33;
x
};
, компилятор выведет тип замыкания как FnMut
.Хотя замыкание возвращает собственную переменную x
, она все равно может вызываться несколько раз, поскольку x
равно Copy
, поэтому компилятор выбирает FnMut
в качестве наиболее общего применимого типа.
При вызовезакрытие FnMut
, само закрытие передается по изменяемой ссылке.Это объясняет ваш первый вопрос - прямой вызов g
не работает, если вы не сделаете его изменяемым, иначе вы не сможете получить изменяемую ссылку на него.Я также неявно ответил на ваш третий вопрос: self
в методах вызова черт Fn
относится к самому замыканию, которое можно рассматривать как структуру, содержащую все захваченные переменные.
При вызове f(g)
, вы передаете FnMut
закрытие g
как FnOnce
закрытие f()
.Это разрешено, поскольку все FnOnce
являются супертрейтом FnMut
, поэтому каждое замыкание, реализующее FnMut
, также реализует FnOnce
.Теперь, когда замыкание преобразовано в FnOnce
, оно также вызывается в соответствии с FnOnce
соглашением о вызовах:
pub trait FnOnce<Args> {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
Закрытие передается значением в этом случае таким образом, вызов потребляет замыкание.Вы можете отдать право собственности на любое ваше значение - оно не должно быть изменчивым, чтобы это работало.
Причина, по которой вы можете вызывать g
несколько раз при вызове через f()
, заключается в том, что g
это Copy
.Он захватывает только одно целое число, поэтому его можно копировать столько раз, сколько вы хотите.Каждый вызов f()
создает новую копию g
, которая используется при вызове внутри f()
.