Entry tags:
Изучаем раст вместе
Я тут недавно узнал, что не только я изучаю ржавчину. Но еще и
juan_gandhi (как минимум)
Первый прикол
Раст имеет понятие мутабельности. Мутабельное менять можно, а иммутабельное нельзя. Приводить одно к другому можно через небезопасный код внутри конструкции unsafe{} - но оптимизатор надеется, что вы не будете так делать. Поэтому если вы так сделаете - он наоптимизирует такого, шо ховайся. Поэтому на самом деле, оно хоть и можно технически - но так делать ненадо.
Мутабельноть может иметь две разные категории, которые могут скомбинироваться четырьмя способами. Если ссылка на структуру мутабельна - то ее (ссылку) можно изменить, чтобы она указывала на другую структуру. А структуру менять при этом нельзя.
А если у вас ссылка на мутабельную структуру - то ссылку менять нельзя, а поля структуры менять можно.
struct Foo{ v: u32 }
let mut foo = Foo{ v: 42 }; //наша структура
let foo1 = Foo{ v:43 };
let mut x = &foo; //мутабельная ссылка на иммутабельную структуру
let y = &mut foo; //иммутабельная ссылка на мутабельную структуру. Поля струтуры менять можно, а переназначить на foo1 нельзя
let mut u = &mut foo; //мутабельная ссылка на мутабельную структуру
let k = &foo; //иммутабельная ссылка на иммутабельную структуру
Второй прикол - это то, что можно мутабельной переменной присвоить иммутабельную и ее менять:
let x = Foo{v:42};
let mut y = x;
y.v = 15;
println!("{}", y.v);
правда, никто не знает, зачем. Но иногда удобно.
Третий прикол
заключается в том, что конструкция let затирает предыдущее имя новым. На самом деле, это будет как бы две разных переменных, просто до предыдущей дотянуться уже не получится, из за одинаковых имен.
let mut foo = Foo{v:42};
let mut foo1 = Foo{v:42};
let y = &mut foo;
let mut y = &mut foo1; // хоба! а говорили нельзя
y.v = 43;
println!("{}", y.v);
проверить: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fad599665db7459ac90b2620ba2386b4
На самом деле, эта фишка работает благодаря фиче NLL (non-lexical lifetimes), которую завезли не так давно. Без нее такое бы тоже получилось, но сложней.
Возникает вопрос
А зачем оно такое нужно?
А нужно оно для оптимизации. И эти правила (насколько понимаю я) порождены такой фичей в gcc как strict aliasing. Заключается она вот в чем. Если у нас, например, есть два укзателя на int, и мы поменяли память по одному указателю - а читаем по другому, то есть два варианта:
1 - указатели указывают на разную область памяти.
2 - указатели указывают на одну область памяти. (перекрывающуюся или совпадающую)
Так вот, обладая знанием о том, что указатели указывают всегда в разные места, компилятор может оптимизировать чтения из памяти, и значение по второму указателю хранить в регистре. Но при этом, если такой код будет работать со случаем 2 - то все сломается.
А если указатели могут указывать в одну область памяти и компилятор это знает, то компилятор вынужден делать каждый раз чтение, дабы быть уверенным, что будет использовано значение, которое в ту ячейку было только что записано.
Например, если у нас есть массив данных и два указателя int*p, int *q внутрь этого массива. И мы пишем и читаем данные из этих указателей, то оптимизировать нельзя! Если такую оптимизацию сделать, то программа будет работать некорректно. Это если кратко. Если подробней - в юнионах строгий алиасинг вообще отключается, но то такое...
Так вот, в раст все эти правила заимствований и неймспейсов написаны так, что компилятор будет все оптимизировать по максимуму (в релизе, разумеется), а менять через два указателя один массив просто не получится.
Но, есть исключение - std::cell::Cell. На Cell может быть несколько указателей, и через каждый можно поменять то, что внутри Cell. Но при этом, чтение данных, которые внутри, осуществляются тоже через эту абстракцию. И компилятор генерирует явное чтение - то есть ничего не ломается!
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Первый прикол
Раст имеет понятие мутабельности. Мутабельное менять можно, а иммутабельное нельзя. Приводить одно к другому можно через небезопасный код внутри конструкции unsafe{} - но оптимизатор надеется, что вы не будете так делать. Поэтому если вы так сделаете - он наоптимизирует такого, шо ховайся. Поэтому на самом деле, оно хоть и можно технически - но так делать ненадо.
Мутабельноть может иметь две разные категории, которые могут скомбинироваться четырьмя способами. Если ссылка на структуру мутабельна - то ее (ссылку) можно изменить, чтобы она указывала на другую структуру. А структуру менять при этом нельзя.
А если у вас ссылка на мутабельную структуру - то ссылку менять нельзя, а поля структуры менять можно.
struct Foo{ v: u32 }
let mut foo = Foo{ v: 42 }; //наша структура
let foo1 = Foo{ v:43 };
let mut x = &foo; //мутабельная ссылка на иммутабельную структуру
let y = &mut foo; //иммутабельная ссылка на мутабельную структуру. Поля струтуры менять можно, а переназначить на foo1 нельзя
let mut u = &mut foo; //мутабельная ссылка на мутабельную структуру
let k = &foo; //иммутабельная ссылка на иммутабельную структуру
Второй прикол - это то, что можно мутабельной переменной присвоить иммутабельную и ее менять:
let x = Foo{v:42};
let mut y = x;
y.v = 15;
println!("{}", y.v);
правда, никто не знает, зачем. Но иногда удобно.
Третий прикол
заключается в том, что конструкция let затирает предыдущее имя новым. На самом деле, это будет как бы две разных переменных, просто до предыдущей дотянуться уже не получится, из за одинаковых имен.
let mut foo = Foo{v:42};
let mut foo1 = Foo{v:42};
let y = &mut foo;
let mut y = &mut foo1; // хоба! а говорили нельзя
y.v = 43;
println!("{}", y.v);
проверить: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fad599665db7459ac90b2620ba2386b4
На самом деле, эта фишка работает благодаря фиче NLL (non-lexical lifetimes), которую завезли не так давно. Без нее такое бы тоже получилось, но сложней.
Возникает вопрос
А зачем оно такое нужно?
А нужно оно для оптимизации. И эти правила (насколько понимаю я) порождены такой фичей в gcc как strict aliasing. Заключается она вот в чем. Если у нас, например, есть два укзателя на int, и мы поменяли память по одному указателю - а читаем по другому, то есть два варианта:
1 - указатели указывают на разную область памяти.
2 - указатели указывают на одну область памяти. (перекрывающуюся или совпадающую)
Так вот, обладая знанием о том, что указатели указывают всегда в разные места, компилятор может оптимизировать чтения из памяти, и значение по второму указателю хранить в регистре. Но при этом, если такой код будет работать со случаем 2 - то все сломается.
А если указатели могут указывать в одну область памяти и компилятор это знает, то компилятор вынужден делать каждый раз чтение, дабы быть уверенным, что будет использовано значение, которое в ту ячейку было только что записано.
Например, если у нас есть массив данных и два указателя int*p, int *q внутрь этого массива. И мы пишем и читаем данные из этих указателей, то оптимизировать нельзя! Если такую оптимизацию сделать, то программа будет работать некорректно. Это если кратко. Если подробней - в юнионах строгий алиасинг вообще отключается, но то такое...
Так вот, в раст все эти правила заимствований и неймспейсов написаны так, что компилятор будет все оптимизировать по максимуму (в релизе, разумеется), а менять через два указателя один массив просто не получится.
Но, есть исключение - std::cell::Cell. На Cell может быть несколько указателей, и через каждый можно поменять то, что внутри Cell. Но при этом, чтение данных, которые внутри, осуществляются тоже через эту абстракцию. И компилятор генерирует явное чтение - то есть ничего не ломается!
no subject
no subject
Надо английский учить. Если бы я лучше знал бы английский, то я конечно активнее участвовал бы в его разработке, и повлиял бы на некоторые моменты, которые мне особо ненравятся. Ну или меня бы убедили, что я не прав.
no subject