#prog #rust
Хозяйке на заметку
Иногда таки возникает такая задача: взять мутабельные ссылки на два элемента слайса. В лоб написать
Первый — обойтись исключительно safe-кодом. В этом случае мы разделяем слайс по индексам и вынимаем ссылки на концы сегментов. Вот один из способов, как это можно сделать:
Другой путь — это использовать немного unsafe, чтобы обойти ограничения borrow checker-а:
Во-первых, мы используем обычный функционал индексирования для того, чтобы получить ссылки на элементы, поэтому индексы проверяются и функция паникует, если какой-то из них окажется за пределами слайса.
Во-вторых, мы явно проверяем при помощи
В-третьих, не смотря на то, что мы образуем ссылку из сырого указателя, благодаря lifetime elision время жизни этой ссылки будет такое же, как и у входной ссылки на слайс. Иными словами, мы не делаем ссылки более долгоживущими, чем надо.
Можете использовать этот код без ограничений.
Хозяйке на заметку
Иногда таки возникает такая задача: взять мутабельные ссылки на два элемента слайса. В лоб написать
let (x, y) = (&mut arr[i], &mut arr[j]);
не получится — borrow checker не даст. И тут есть два пути.Первый — обойтись исключительно safe-кодом. В этом случае мы разделяем слайс по индексам и вынимаем ссылки на концы сегментов. Вот один из способов, как это можно сделать:
fn get_two_mut<T>(
arr: &mut [T],
idx1: usize,
idx2: usize,
) -> (&mut T, &mut T) {
let (ifirst, isecond) = (
idx1.min(idx2),
idx1.max(idx2),
);
let (first, last) = match &mut arr[ifirst..=isecond] {
[first, .., last] => (first, last),
_ => panic!("overlapping indices"),
};
if ifirst == idx1 {
(first, last)
} else {
(last, first)
}
}
Это рабочее решение, но тут многовато операций, да и перестановка ссылок в соответствие с индексами выглядит некрасиво.Другой путь — это использовать немного unsafe, чтобы обойти ограничения borrow checker-а:
fn get_two_mut<T>(
arr: &mut [T],
idx1: usize,
idx2: usize)
-> (&mut T, &mut T) {
assert_ne!(idx1, idx2);
let a: *mut T = &mut arr[idx1];
let b: *mut T = &mut arr[idx2];
unsafe { (&mut *a, &mut *b) }
}
Что же делает эту реализацию sound?Во-первых, мы используем обычный функционал индексирования для того, чтобы получить ссылки на элементы, поэтому индексы проверяются и функция паникует, если какой-то из них окажется за пределами слайса.
Во-вторых, мы явно проверяем при помощи
assert_ne!
, что индексы не равны, поэтому возвращаемые ссылки заведомо не указывают на одну и ту же область памяти.В-третьих, не смотря на то, что мы образуем ссылку из сырого указателя, благодаря lifetime elision время жизни этой ссылки будет такое же, как и у входной ссылки на слайс. Иными словами, мы не делаем ссылки более долгоживущими, чем надо.
Можете использовать этот код без ограничений.
Telegram
Блог*
#prog #rust #моё
В Rust в некоторых случаях можно использовать анонимный лайфтайм ('_). Практика показывает, что эта фича некоторым не до конца понятна, поэтому я решил написать об этом подробнее.
Времена жизни — пожалуй, именно та фича, которая больше…
В Rust в некоторых случаях можно использовать анонимный лайфтайм ('_). Практика показывает, что эта фича некоторым не до конца понятна, поэтому я решил написать об этом подробнее.
Времена жизни — пожалуй, именно та фича, которая больше…
👍7
Forwarded from Блог погромиста
В рот мне декоратор! Сегодня мне написали товарищи с Хабра - я, оказывается, победил в технотексте-2021 со своей статьёй про собеседование в Яндекс 🤷♂ И пока я жду грузовики, наполненные доверху бабосами (хотя, кажется, подарят всё-таки просто толстовку), продолжаю писать новую статью. Писать статьи сложно, и, к сожалению, идей у меня значительно больше, чем сил для их написания. Ну штош... Дорогу осилит идущий.
https://habr.com/ru/post/550088/
https://habr.com/ru/post/550088/
👍3
#prog #scala #article
Есть статья от Олега Нижника об экспериментальной новой фиче в Scala 3 — capture checking, опубликованная 14 февраля.
Есть OTUS, которая на Хабре известна своими многочисленными дерьмовыми переводами.
Что мы получим, если их соединим?
Комедию в двух актах!
Акт первый (перевод статьи от OTUS от 5 марта, с плохим переводом).
Акт второй (перевод той же самой статьи от OTUS от 7 июня, с другим, но всё ещё плохим переводом).
Клоуны, блин. 🤡
Есть статья от Олега Нижника об экспериментальной новой фиче в Scala 3 — capture checking, опубликованная 14 февраля.
Есть OTUS, которая на Хабре известна своими многочисленными дерьмовыми переводами.
Что мы получим, если их соединим?
Комедию в двух актах!
Акт первый (перевод статьи от OTUS от 5 марта, с плохим переводом).
Акт второй (перевод той же самой статьи от OTUS от 7 июня, с другим, но всё ещё плохим переводом).
Клоуны, блин. 🤡
Medium
About capture checking
in scala 3
😁5
#ml #video
youtube.com/watch?v=efPrtcLdcdM
Aka "Я обучил языковую модель на 4chan, сделал бот, который давал ответы с её помощью, и посмотрел, что произойдёт"
(thanks @DogeShibu)
youtube.com/watch?v=efPrtcLdcdM
Aka "Я обучил языковую модель на 4chan, сделал бот, который давал ответы с её помощью, и посмотрел, что произойдёт"
(thanks @DogeShibu)
YouTube
GPT-4chan: This is the worst AI ever
#gpt4chan #4chan #ai
GPT-4chan was trained on over 3 years of posts from 4chan's "politically incorrect" (/pol/) board.
(and no, this is not GPT-4)
EXTRA VIDEO HERE: https://www.youtube.com/watch?v=dQw4w9WgXcQ
Website (try the model here): https://gpt…
GPT-4chan was trained on over 3 years of posts from 4chan's "politically incorrect" (/pol/) board.
(and no, this is not GPT-4)
EXTRA VIDEO HERE: https://www.youtube.com/watch?v=dQw4w9WgXcQ
Website (try the model here): https://gpt…
🔥4👎2👍1
Блог*
#prog #rust Хозяйке на заметку Иногда таки возникает такая задача: взять мутабельные ссылки на два элемента слайса. В лоб написать let (x, y) = (&mut arr[i], &mut arr[j]); не получится — borrow checker не даст. И тут есть два пути. Первый — обойтись исключительно…
GitHub
Expose `get_many_mut` and `get_many_unchecked_mut` to HashMap by Urgau · Pull Request #94647 · rust-lang/rust
This pull-request expose the function get_many_mut and get_many_unchecked_mut from hashbrown to the standard library HashMap type. They obviously keep the same API and are added under the (new) map...
#prog #rust
В стандартной библиотеке есть тип core::alloc::Layout. Главное его назначение — описывать форму запроса памяти у аллокатора (см., например, alloc::alloc::alloc). Одного лишь размера для подобных функций недостаточно, поскольку для корректного обращения по указателю нужно также, чтобы указатель был выравнен. Можно считать, что
Вывод:
В принципе, на этом можно было бы и закончить, но давайте запустим этот же код, выводящий размеры, на nightly (на момент написания — версия
и выведем его размер, то снова получим
Откроем исходники. Поле
Итого мы получаем, что
И напоследок. Судя по PR, который это внёс, это изменение будет уже буквально в следующей стабильной версии, 1.62. Ну и, так как тип
В стандартной библиотеке есть тип core::alloc::Layout. Главное его назначение — описывать форму запроса памяти у аллокатора (см., например, alloc::alloc::alloc). Одного лишь размера для подобных функций недостаточно, поскольку для корректного обращения по указателю нужно также, чтобы указатель был выравнен. Можно считать, что
Layout
состоит из двух usize
— и конструктор from_size_align, который эти два usize
и принимает, только укрепляет эти подозрения. Посмотрим, как это выглядит на практике, проверив размеры:println!("{}", size_of::<usize>());
println!("---");
println!("{}", size_of::<Layout>());
println!("{}", size_of::<Option<Layout>>());
println!("{}", size_of::<Option<Option<Layout>>>());
Вывод:
8
---
16
16
24
Ага, Layout
занимает столько же места, сколько и два usize
... Стоп, а почему Option<Layout>
занимает столько же места, сколько и Layout
? Выходит, там внутри что-то другое, раз нашлось место для тега. И действительно, если мы откроем исходники из документации, то увидим, что Layout
выглядит так (за вычетом комментариев и аннотаций):pub struct Layout {
size_: usize,
align_: NonZeroUsize,
}
Логично. Выравнивание никогда не может быть равно нулю — разумно это значение хранить в NonZeroUsize. Также логично, что дважды вложенный Option
занимает уже больше места — места для дискриминанта в нишах Layout
хватает лишь на одно дополнительное значение.В принципе, на этом можно было бы и закончить, но давайте запустим этот же код, выводящий размеры, на nightly (на момент написания — версия
2022-06-09 420c970cb1edccbf60ff
):8
---
16
16
16
Внезапно вложенный Option
занимает столько же байтов, сколько и Layout
. Более того, если мы сделаем такой тип:enum ManyVariants {
_Layout(Layout),
_Variant1,
// опущено 98 вариантов
_Variant100,
}
и выведем его размер, то снова получим
16
! В чём же дело?Откроем исходники. Поле
align
имеет тип ValidAlign
, который лежит в core::mem
. Тип ValidAlign является одноместной кортежной структурой с #[repr(transparent)]
с полем типа ValidAlignEnum
. ValidAlignEnum имеет определение, зависящее от битности целевой архитектуры:#[cfg(target_pointer_width = "16")]
type ValidAlignEnum = ValidAlignEnum16;
#[cfg(target_pointer_width = "32")]
type ValidAlignEnum = ValidAlignEnum32;
#[cfg(target_pointer_width = "64")]
type ValidAlignEnum = ValidAlignEnum64;
Приведём определение, скажем, ValidAlignEnum16
(самого маленького из типов):#[derive(Copy, Clone)]
#[repr(u16)]
enum ValidAlignEnum16 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
_Align1Shl3 = 1 << 3,
_Align1Shl4 = 1 << 4,
_Align1Shl5 = 1 << 5,
_Align1Shl6 = 1 << 6,
_Align1Shl7 = 1 << 7,
_Align1Shl8 = 1 << 8,
_Align1Shl9 = 1 << 9,
_Align1Shl10 = 1 << 10,
_Align1Shl11 = 1 << 11,
_Align1Shl12 = 1 << 12,
_Align1Shl13 = 1 << 13,
_Align1Shl14 = 1 << 14,
_Align1Shl15 = 1 << 15,
}
ValidAlignEnum32
и ValidAlignEnum64
имеют аналогичные определения, только вариантов побольше. Конструкторы ValidAlign
принимают на вход usize
и вызывают на нём transmute в ValidAlignEnum
.Итого мы получаем, что
ValidAlign
— это тип, который имеет тот же размер, что и usize
, но в качестве валидных битовых паттернов имеет только степени двойки. Помимо кучи ниш, которые можно использовать для дискриминанта, это позволяет miri проверять, что в небезопасных конструкторах Layout
— не проверяющих выравнивание — аргумент align
действительно является степенью двойки, причём просто за счёт проверки корректности представления типов.И напоследок. Судя по PR, который это внёс, это изменение будет уже буквально в следующей стабильной версии, 1.62. Ну и, так как тип
ValidAlign
полезен, возможно, в будущем этот тип попадёт в публичное API std
.doc.rust-lang.org
Layout in core::alloc - Rust
Layout of a block of memory.
👍5
#prog #rust #rustlib #article
Announcing error-stack, a context-aware error library for Rust that supports arbitrary attached user data
Вы не поверите, но ещё одна Rust-библиотека для ошибок. Вместе со статьёй, в которой описывается мотивация для её создания и дизайна
Announcing error-stack, a context-aware error library for Rust that supports arbitrary attached user data
Вы не поверите, но ещё одна Rust-библиотека для ошибок. Вместе со статьёй, в которой описывается мотивация для её создания и дизайна
HASH for Developers
Announcing error-stack – HASH Developer Blog
A context-aware error library for Rust that supports arbitrary attached user data
👍2🎉1
#prog #rust #serde
В serde_json есть тип Value, который может представить любое валидное JSON-значение. Есть парочка вещей, которые не столь известны и стоят упоминания.
1.
2. В
3.
4. Если вам нужно достать глубоко вложенное значение, можно вместо череды индексаций или вызовов
6. У
В serde_json есть тип Value, который может представить любое валидное JSON-значение. Есть парочка вещей, которые не столь известны и стоят упоминания.
1.
Value
, а также обе разновидности ссылок на него могут быть сравнены на равенство со всеми примитивными числами, bool
, str
и String
.2. В
Value
можно сконвертировать из множества различных типов, которые могут входить в его состав, а также составлять из итератора по элементам и итератора по элементам вместе с ключами.3.
Value
можно индексировать как строками, так и числовыми индексами, при этом в случае, если индексировать значение таким образом нельзя, иммутабельное индексирование вместо паники возвращает ссылку на Value::Null
.4. Если вам нужно достать глубоко вложенное значение, можно вместо череды индексаций или вызовов
get
использовать метод pointer/pointer_mut, которые достают значение по указанному строковому пути:let data = json!({
"x": {
"y": ["z", "zz"]
}
});
assert_eq!(data.pointer("/x/y/1").unwrap(), &json!("zz"));
assert_eq!(data.pointer("/a/b/c"), None);
5. Value
и &Value
реализуют Deserialize
. Это значит, что если у вас есть разобранный JSON в неструктурированном виде, вы можете сконвертировать его в свои типы, реализующие Deserialize
— причём в случае со ссылкой у вас при этом останется изначальный JSON. Так можно пробовать десериализовывать поочерёдно в несколько типов, и это может оказаться дешевле, чем парсить всё с нуля.6. У
Value
есть собрат RawValue. Этот тип фактически представляет собой синтаксически валидный JSON, который хранится в виде слайса на входную строку. Его можно использовать для того, чтобы проводить манипуляции над JSON и при этом не хранить разобранный JSON в памяти целиком:use serde::{Deserialize, Serialize};
use serde_json::{Result, value::RawValue};
#[derive(Deserialize)]
struct Input<'a> {
code: u32,
#[serde(borrow)]
payload: &'a RawValue,
}
#[derive(Serialize)]
struct Output<'a> {
info: (u32, &'a RawValue),
}
// Efficiently rearrange JSON input containing separate "code" and "payload"
// keys into a single "info" key holding an array of code and payload.
//
// This could be done equivalently using serde_json::Value as the type for
// payload, but &RawValue will perform better because it does not require
// memory allocation. The correct range of bytes is borrowed from the input
// data and pasted verbatim into the output.
fn rearrange(input: &str) -> Result<String> {
let input: Input = serde_json::from_str(input)?;
let output = Output {
info: (input.code, input.payload),
};
serde_json::to_string(&output)
}
fn main() -> Result<()> {
let out = rearrange(r#" {"code": 200, "payload": {}} "#)?;
assert_eq!(out, r#"{"info":[200,{}]}"#);
Ok(())
}
docs.rs
Value in serde_json - Rust
Represents any valid JSON value.
👍10