amarao (amarao_san) wrote,
amarao
amarao_san

Category:

something-flow in Rust

Помните, недавний пост, где я рассказывал про величие Бейсика в вопросах обучения программированию? Мол, объясняет переменные, значения, control flow.

А вот вчера я два часа скрипел мозгом, разбираясь с generic'ами в районе impl. Я, вроде, разобрался, а в процессе разбирательства я не мог не заметить похожести происходящего со скрипом в голове у тех, кто только учит программирование.




(некоторое понимание rust'а для дальнейшего требуется).

В Rust'е generic'и, точнее, типопараметры (тип, который передаётся как параметр), имеют свой flow. Он не control (поскольку не касается runtime), но у него однозначно есть направление, т.е. есть значения «до» и «после». Поскольку это не runtime, у нас нет «time», так что понятие «до» и «после» весьма условны, и их можно было бы называть «причиной» и «следствием», но проблема с том, что они инвертированы — скорее, это «требование» и «выполнения требования».

Вот на осознании наличия этого requirements flow я и скрипел страшно мозгами, переодически улетая в higher order kindnessness с руганью мол, «и тут ассоциированные типы не поддерживаются, и так неправильно».

Суть откровения я написал уже выше: есть requirements flow при компиляции. Я не могу точно показать порядок его выполнения (т.к. я не ощущаю все случаи, когда он происходит), но вот частный случай:


struct Foo<Fn(i32)->i32> {
   foo: T
}



Что тут написано? Что структура Foo принимает параметр T (типопараметр), с ограничением, что T умеет Fn(i32)->i32 (т.е. либо функция, либо замыкание из i32 в i32). Вот это "принимает" надо смотреть с большой осторожностью, потому что это всё-таки не настоящий вызов, а всего лишь ограничение.

Теперь, impl.

impl<F:Fn(i32)->i32> Foo<F> {
   fn new(a: F) -> Self {
      foo{foo: a}
   }
}


Первая строчка - это же ад с точки зрения разбора. impl у нас задаёт требование к impl. Мы не можем использовать new с типом, для которого нет Fn(i32)->i32. Но в то же самое время он передаёт этот F в Foo, и он "обещает" struct'у Foo, что F - именно Fn(i32)->i32. Если убрать это обещание, то struct Foo будет ругаться: expected an `Fn<(i32,)>` closure, found `F`.

Очень важно, что тут impl тут выступает как активный (?) участник, а Foo<F> - как пассивный (?) - он всего лишь говорит, что мы работаем с Foo<F>.

При этом у нас есть ещё type parameter для функции, который может использоваться для наложения доп условий, но не для их предоставления. Вот этот код не работает:

struct Foo<T: Fn(i32)->i32> {
   foo: T
}

impl<F> Foo<F>{
  fn new(a: F)->Self
  where F: Fn(i32)->i32
  {
    Foo{foo:a}
  }
}

А вот этот - работает. В нём функция более строга, чем структура и impl:

struct Foo<T> {
   foo: T
}

impl<F> Foo<F>{
  fn new(a: F)->Self
  where F: Fn(i32)->i32
  {
    Foo{foo:a}
  }
}
fn main() {
    println!("Hello, world!");
    let _a = Foo::new(|x|{x});
}

Вот в понимании этого - кто кому ограничения выставляет, я реально страшно скрипел мозгами. Даже сейчас скриплю, пока пишу, потому что всё жутко шатко. Например, я только что обнаружил, что типопараметр new не может удовлетворить trait requirement для структуры, и надо обязательно в impl. Понятно, но не интуитивно - интуиции ещё нет.
Tags: rust
Subscribe

  • Админская мудрость

    Когда вывод strace на башовый скрипт становится понятнее самого скрипта, граница разумности давно пройдена.

  • Rules of internet

    Rule 34. There is porn of it. Rule 35. It's used to mine cryptocurrencies.

  • CI без сервера

    А вот у меня есть такой запрос: я хочу иметь CI-подобный инструмент (задачи/автоматически вычисляемые зависимости порядка выполнения, параметры,…

  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 2 comments