DEV: Рубиновые тона
3.21K subscribers
142 photos
2 videos
8 files
966 links
Анонсы новых видео о программировании (Ruby/Rails, Solidity/Ethereum, Python, JS и не только), практические советы, обзор полезных инструментов и новости из мира IT
Download Telegram
Как уже отметил наш постоянный зритель несколько месяцев назад, токены Толкина действительно были пущены в оборот. Кто бы мог подумать пару лет назад... (отсылка к старым стримам по ERC20) https://www.reddit.com/media?url=https%3A%2F%2Fi.redd.it%2Fjust-arrived-in-the-mail-today-v0-fkqtrcac6wgc1.jpeg%3Fs%3D9df19cbd93dcc9ece791fb096a2e6d15e0a9d7ec
😁7👍1
Некоторые замечают, что при использовании Metamask в тестовой среде с Hardhat происходят какие-то дополнительные странные вызовы. В частности, если попытаться отправить обычную транзакцию, в журнале Hardhat может вылезти ещё штук пять сообщений вроде "unrecognized selector". Откуда это берётся?

Я провёл небольшое расследование, и дело в том, что Metamask пытается вызывать функции supportsInterface() (ERC165), а также функции типа decimals() и symbol(), тк совпадают селекторы, к примеру bytes4(keccak256(bytes("symbol()"))) вернёт 0x95d89b41, это как раз один из неопознанных селекторов. Любопытно, что это вызывается, даже если вы не используете ERC20.

Так что варианта два - просто игнорировать, либо добавить пустую функцию fallback (плюс добавить наследование от контракта ERC165). Ну, то есть можно сделать пустые функции decimals, symbol, но это как-то странно, если контракт не связан с токенами.
🔥10👍1
И тут я вспоминаю об этом нашем кавере и хочется танцевать так, как будто никто не видит (тем более, что никто и не видит) https://soundcloud.com/ravens-die-laughing/zvezdy
👍9😱2😐2
По этой теме было много вопросов, так что во вторник проведём стрим.

Мы поговорим о новом весьма хайповом "стандарте" ERC404, который на самом деле и не стандарт вовсе. В любом случае, это подход позволяет создавать комбинацию токенов ERC20 и ERC721, вводя для NFT понятие "долей". Мы разберём, в чём польза этого подхода и как он работает.

https://youtube.com/live/7TZFpbge83A
🔥7👍21👏1
Что ж, цикл "Воспоминания" Тэффи завершён. К сожалению, завершился он на довольно грустной ноте, которая подозрительно похожа на происходящее сегодня, так что добавил ещё две бонусные главы. В ближайшие дни ещё будут "воспоминания" о таком известном человеке, как Григорий Распутин, а также об Алексее Толстом (это который "не Лев") https://youtu.be/GOx8e0-8Tws
4👍3🆒21
Вышел Hardhat 2.20 с поддержкой предстоящего хард-форка Cancun (выходит в середине марта), который должен добавить быстродействия и безопасности. Также поддерживается Solc 0.8.24. https://github.com/NomicFoundation/hardhat/releases/tag/hardhat%402.20.0
👍7
Ребята из MIT выложили некоторые свои курсы в открытый доступ (но там, кажется, ограниченный срок записи). Вот, к примеру - https://www.edx.org/learn/computer-science/massachusetts-institute-of-technology-introduction-to-computer-science-and-programming-using-python Это самые основы, для тех, кому не хватает фундамента (кое-что из этого мы разбирали в плейлисте Алгоритмы)
🔥15
Придумал забавный "тест ложки" для английского. Грубо говоря, если вы на 100% понимаете этот текст, то у вас уже вполне приличный уровень языка
🔥116🤯4💯1
Продолжаем конкурс лучших комментариев. В этот раз из закрытого чата, куда пригласили постримить. And the Oscar goes to...
🤣28😁8👍3😎31🤝1
В этом уроке мы поговорим про паттерн Extension, который можно использовать, если ваш контракт слишком громоздкий и не вписывается в максимально допустимый размер Ethereum. Этот паттерн довольно простой, но позволяет эффективно решать данную проблему. https://www.youtube.com/watch?v=p-XgxfCB50I
🔥10🙏1
Недавно написал пост, в котором исследуется любопытный вопрос - как вытащить все страницы сайта для последующей обработки? Перечислены разные варианты, в том числе с помощью скрипта на Python https://www.scrapingbee.com/blog/how-to-find-all-urls-on-a-domains-website-multiple-methods/
👍81
Последний наверное год-полтора мы периодически заходили в местную китайскую забегаловку с аутентичной едой и такой непринуждённой атмосферой лёгкой неприбранности. Моя жена сильно похожа на китаянку, так что мы в один момент как-то разговорились с владелицей заведения, и с тех пор каждый раз перетирали за жизнь. Конечно, на английском, хотя на базовом уровне она латышский тоже освоила. Да и я, сказать честно, не мастер в этом плане - хотя тут мне пеняют на то, что я и в русском ударению ставлю неправильно. Что поделать, мог бы вообще говорить с характерным акцентом.

Мне казалось, что имя Элайджа совсем простое, но нашей знакомой оно показалось каким-то сверх-сложным 😂 Помню ещё, я пытался провести аналогию с Элайджей Вудом - актёром из Властелина колец, но, оказывается, этот фильм совершенно неизвестен в Китае. Ну, я, как фанат Толкина, был уверен, что профессора знают по всему миру 🤓

Вообще, интересно подмечать, как всё-таки люди отличаются друг от друга, но всё равно могут найти общий язык. Поэтому, думается, стоит путешествовать, общаться с представителями разных культур. И поэтому, к сожалению, тоталитарные режимы пытаются оградить граждан от "тлетворного влияния". Потому что когда ты знакомишься с людьми "оттуда", ты понимаешь, что с ними вполне можно взаимодействовать...

К сожалению, с начала года, проходя мимо этого заведения, мы всё время видели за прилавком неизвестную девушку, кажется, местную. Раньше периодически "на хозяйстве" была дочка владельцев, но теперь и она куда-то исчезла. А на той неделе я с удивлением обнаружил, что на здании сменилась вывеска, и там теперь какие-то кебабы (kebabs, это шаурма). Искал в интернете какие-то сведения, ничего не нашёл. Странно, и жаль.

А тут ещё другие азиатские друзья - корейский ресторан - тоже переехал чёрт знает куда, только на велосипеде доедешь. Как-то всё разом 🤪

Это, конечно, в общем и целом пост наигранной весёлости, так как весёлого мало, но жаль, когда люди, с которыми ты, вроде, был в каком-то контакте, исчезают. https://www.youtube.com/watch?v=bsTxR64s5Kc
😢3👍2🌭1
В этом уроке мы поговорим о EIP2929 и EIP2930, о газе и его оптимизации. Мы узнаем, что такое холодное и горячее (hot/warm) обращение и почему это важно. Кроме того, мы узнаем, что такое accessList в транзакциях и как он может помочь заранее "разогреть слоты" для экономии газа. В конце мы используем этот подход в паттерне Extension. https://www.youtube.com/watch?v=RRXLzfUgcLE
🔥102👏1
К вопросу об указателях, в том числе и умных, в Rust

Система владения и заимствования в Rust, а также наличие разнообразных указателей может сильно запутать поначалу, особенно когда выясняется, что указатели бывают умные, а бывают - не очень. Это не говоря о том, что у нас есть referencing, а есть ещё dereferencing, и всё это вместо нужно как-то увязать. Попробуем разобраться.

Есть вот такой код:

let a = 5;
let b = &a;


a будет иметь тип i32, эта переменная "знает", где лежит число 5. Так как это простой тип данных, то число лежит в стеке. Кроме того, помним, что a "владеет" этим числом и в нужный момент должна удалить соответствующие данные.

В случае с b мы используем оператор referencing &, то есть фактически делаем указатель. Тип b будет &i32, эта переменная знает, у кого спросить, где находится нужное число (знает это а). При этом b не получает владение этим числом. Больше того, мы не можем даже сравнить а и b напрямую, код a == b; вернёт ошибку - типы разные, мы не можем сравнить само число i32 и указатель на него.

Однако никто не мешает нам сказать "дай мне то значение, которое лежит по этому указателю". Для этого используется оператор dereferencing, "звёздочка":

a == *b;


Теперь сравнение работает. Причём мы можем даже присвоить это значение другой переменной:

let mut c = *b;
c = c + 1;


Но значит ли это, что изменение c приведёт к измению a и/или b? На самом деле, нет. Мы опять же работаем с простым типом данных, и присваивание приводит к тому, что значение копируется для другой переменной, то есть a и c имеют разные, независимые значения.

С типами вроде vec и string ситуация похожая, но становится несколько сложнее.

let a = vec![1, 2, 3];


Мы создали вектор из трёх элементов, и мы знаем, что он будет хранится в heap, потому что потенциально его длина может меняться. В стек Rust может поместить только значения, размер которых точно известен во время компиляции, и вектор к такому типу (в отличие от фиксированного массива) не относится.

Но тогда вопрос: что же содержит в себе переменная a? Она по крайней мере должна знать, с какого адреса в heap начинается наш вектор и какой у него размер. То есть выходит, что это тоже какой-то указатель. Но "простой" указатель в духе &b данными не владеет, а просто ссылается на них. В нашем же случае мы явно ожидаем, что a владеет вектором, ведь кто-то же должен в итоге очистить память.

Ответ заключается в том, что vector (как и string) - это умный указатель, smart pointer, который действительно указывает на heap, но при этом владеет данными там и имеет пристыкованные метаданные.

Значит, если мы напишем

let b = &a;


то мы фактически тоже создадим указатель, который в свою очередь будет ссылатьcя на "умный указатель", и это, как говорится, две большие разницы.

К подобным типам данных тоже можно попытаться применить dereferencing, но вот такая строка выдаст ошибку:

let c = *b;


В отличие от простых типов данных, вектора и строки при присваивании не копируются, а переносятся. То есть к примеру вот такое

let a = vec![1, 2, 3];
let d = a;


приведёт к тому, что вектор "перенесётся" в d, ну то есть на него теперь указывает другая переменная. Когда же мы делаем let c = *b;, то мы фактически пытаемся через b влезть в данные a и сделать перемещение. Компилятору такой манёвр не сильно нравится, а скопировать весь вектор автоматом тоже не выходит, такую команду мы должны отдать сами:

let c = (*b).clone();


Это сработает, но мы именно полностью копируем вектор, он будет независим от вектора a.

Но тут можно задать новый вопрос - раз a тоже является указателем, можем мы сможем сделать dereferencing для него? Можно попробовать:

let b = *a;


Тип b определится как [i32], но программа не скомпилируется. Фактически мы пытается влезть напрямую в данные, на которые указывает умный указатель, и это получается просто массив. Массивы в Rust фиксированные и лежат в стеке, но Rust не может сунуть этот конкретный массив в стек, потому что неизвестна его длина в текущий момент времени!

Если вы думаете, что это всё, то ничуть не бывало. Есть ещё метод, который называется .deref():
👍6
let a = String::from("test");
let b = a.deref();


Тип b будет &str (если бы там был вектор, тип бы стал &[i32]). Чувствуете разницу? Этот метод сделает указатель непосредственно на данные, но он не попытается эти данные куда-то перенести. Если же перед deref поставить звёздочку, то мы опять получим ошибку: *(a.deref()), потому что опять будет сделана попытка вытащить сами данные из heap и куда-то их перетащить. Фактически, здесь *(a.deref()) равносильно *a.

При этом для простых типов deref не имеет смысла. На типах вроде i32 его нет вовсе, на указателях это метод просто вернёт сам указатель:

let a: i32 = 5;
let b = &a;
let c = b.deref();


Тип c будет &i32, как у b.

Интересно, что в ряде случаев Rust умеет делать dereference автоматически для нашего же удобства. К примеру, есть функция:

fn call(s: &str) {
println!("{}", s);
}


Она может принять как &str, так и String:

let a = "test";
let b = String::from(a);

call(a);
call(&b);


Работает это потому, что у String есть trait Deref (про него будет в видео), который умеет делать dereferencing в обычный &str.

То есть функция делает что-то такое:

let borrowed = &b;
let my_str = c.deref();


Тип my_str будет &str, то есть ровно то, что написано в функции. Хотя мы могли бы сделать слайс &str сами, это уж очень неудобно, ведь нужно получить сами данные из умного указателя, сделать заимствование, и потом создать слайс:

let borrowed = &b;
let my_str = &(*borrowed)[..];


А так deref() вызовется для нас, причём столько раз, сколько требуется, поэтому если функция принимает массив вида &[i32], то туда можно запихнуть и вектор с числами того же типа.

Теперь мы понимаем, какие есть указатели и как из них получать данные. Интересно, что мы можем создавать и свои умные указатели в Rust, и самым простым является Box - он положит наши данные в heap и сделает привязку к ним по аналогии с тем, что делает vector или string:

let a: Box<i32> = Box::new(42);


То есть наши число теперь торчит не в стеке, а в heap, и a содержит в себе умный указатель. Это значит, что мы можем тоже делать referencing и dereferencing:

let a: Box<i32> = Box::new(42);
let b: &Box<i32> = &a;
let c: &i32 = a.deref();
let d: i32 = *a;


Из аннотации типов можно легко видеть, где что получается. Кстати, *a тут тоже работает в отличие от истории с векторами и строками, потому что размер целого числа точно известен, его можно скопировать и поместить в стек. То есть в d будет число, независимое от того, на которое указывает a (там вообще выходит, что он сначала делает deref, потом звёздочку).
Однако вот так всё равно сделать нельзя: let e = *b;, как и в предыдущих примерах. *b укажет на сам Box<i32>, копировать его автоматом Rust не будет, так что там придётся написать clone()

Но тут вопрос: зачем нам вообще этот box нужен, если лучше хранить простые типы в стеке, который банально быстрее работает? Это так и есть, во многих случаях хранить обычное число "в коробке" смысла нет. Но если у нас есть данные, размер которых заранее неизвестен, то это может быть очень актуально.

К примеру, есть вот такая структура, описывающая кошку:

struct Cat {
name: String,
age: u8,
}

let cat = Cat {
name: String::from("Mr. Buttons"),
age: 3,
};


Где будет хранится наш Mr. Buttons? Сама структура будет в стеке, но строка как таковая будет в heap (в стеке же будет указатель на неё).

Однако предположим, что у кошки может быть предок, и это тоже кошка:

struct Cat {
name: String,
age: u8,
parent: Option<Cat>,
}


Можно ли такую структуру запихнуть в стек? Нет, потому что она рекурсивная, и её размер во время компиляции определить невозможно. Дело в том, что у Mr. Buttons может быть потомок, у того свой потомок, и так до бесконечности. Поэтому родителя нужно хранить в heap, и это тот случай, когда нам нужен Box:

struct Cat {
name: String,
age: u8,
parent: Option<Box<Cat>>,
}


Создадим двух кошек:
👍5
let cat_parent = Box::new(Cat {
name: String::from("Parent Cat"),
age: 3,
parent: None,
});

let cat = Cat {
name: String::from("Mr. Buttons"),
age: 3,
parent: Some(cat_parent),
};


Можно сделать так, можно было первую кошку сделать без Box, и добавить Box::new только для parent у Mr Buttons.

Но с таким подходом есть проблема: cat_parent будет недоступен после создания Mr Buttons, тк там происходит move (перемещение). Вместо этого можно сделать clone: parent: Some(cat_parent.clone()),. Это, правда, может быть не сильно оптимально, так как мы копируем всю структуру сразу, плюс фактически родитель для Mr Buttons и Parent Cat не связаны между собой (изменение возраста Parent Cat не приведёт к изменению возраста родителя).

Можно попробовать добавить заимствование для родителя, но тогда придётся прописывать lifetimes. Если же мы хотим, чтобы родитель был связан с существующей кошкой, плюс к тому, изменения кошки приводило бы к изменению родителя, то нам потребуется использовать другие умные указатели. Их мы рассмотрим в следующем уроке.
👍5🔥31🤯1