Почему я могу записать тип функции в параметре типа структуры?

#rust #closures #typing #monomorphism

Вопрос:

Если я правильно понимаю, в Rust каждый тип закрытия имеет уникальный тип, который не может быть записан. Я также думал, что это применимо к функциям, однако я могу сделать следующее, в котором я явно пишу параметр типа в возвращаемых типах get_struct_1 и get_struct_2 :

 struct FooStructlt;Fgt; where F: Fn(i32) -gt; i32 {  f: F, }  fn foo(x: i32) -gt; i32 {  2*x }  fn bar(x: i32) -gt; i32 {  -1*x }  fn get_struct_1() -gt; FooStructlt;fn(i32) -gt; i32gt; {  FooStruct { f: foo } }  fn get_struct_2() -gt; FooStructlt;fn(i32) -gt; i32gt; {  FooStruct { f: bar } }  // This does not work - the trait has to be boxed //fn get_struct_3() -gt; FooStructlt;Fn(i32) -gt; i32gt; //{ // FooStruct { f: |x| 10*x } //}  fn main() {  let mut x = get_struct_1();  // Why does this work - do bar and foo have the same type?  x = get_struct_2();  // Why does this work - doesn't a closure have its own unique, unwriteable type?  x = FooStruct { f: |x| 10*x };   let mut y = FooStruct { f: |x| 10*x };  // Does not work - no two closures have the same type.  //y = FooStruct { f: |x| 10*x };  // Does not work - even though the 'other way around' worked with x.  // But _does_ work if I type-annotate y with FooStructlt;fn(i32) -gt; i32gt;  //y = get_struct_1(); }  

Я думал, что Rust мономорфен в том, как он обрабатывает параметры типа. Так что, если я сделаю это

 struct FooStruct {  f: Boxlt;dyn Fn(i32) -gt; i32gt; }  

программа будет динамически определять, что f запускать во время выполнения, но FooStructlt;Fgt; версия позволяет избежать динамической отправки.

Этот пример, по-видимому, не согласен с этим. Если x = get_struct_2(); бы строка находилась внутри if инструкции, компилятор не смог бы определить x , содержит ли она обернутую версию функции foo или bar .

Ответ №1:

Замыкания (и функции, если на то пошло) действительно имеют уникальные, неписаные типы. Однако они также могут быть применены (и неявно* тоже) к указателям функций, когда они не захватывают никаких переменных, чего не делают ваши. По сути, именно по этой причине это работает:

 fn main() {  // closure is inferred to be a function pointer  let mut f: fn() -gt; i32 = || 5;  // assigning a different function pointer  f = || 6; }  

Но это не так:

 fn main() {  // closure is inferred to be a unique closure type  let mut f = || 5;  // uh oh! different closure type, errors  f = || 6; }  

* это не столько неявное приведение, сколько неявный вывод типа

Комментарии:

1. Хорошо, это немного помогает. Но означает ли это, что в вашем первом примере динамическая отправка происходит при f вызове?

2. Не совсем. f сохраняет указатель на функцию, на что бы она ни указывала, вызывается при вызове f . Вы могли бы сказать, что это динамическая отправка в том смысле, что то, на что f указывает, может измениться, но при этом не задействованы переменные, как при работе с dyn Trait