Язык Solidity - Урок 2: Типы данных
Язык Solidity: Типы данных (Урок 2)
О чем будет урок
В отличии от PHP или JavaScript язык Solidity является типизированным. Это означает, что перед объявлением переменной нужно указать ее тип. Сегодня мы рассмотрим типы переменных, которые бывают в Solidity. Типы делятся на простые (типы значений) и ссылочные. Разница между ними в том, что при присваивании или передаче параметром в функцию первые копируют хранящееся в них значение, а вторые передают лишь ссылку на место, где хранится значение, на которое они ссылаются (указывают). Разберем их по-порядку.Логический тип (bool)
Этот тип представляет может хранить одну из двух констант TRUE или FALSE. На самом деле в solidity есть неявное преобразование типов и числовые значения больше нуля он относит к TRUE, а 0 к FALSE. Вот небольшой пример:contract TypesTest {
// Логический тип
function fxbool(bool arg) returns (string) {
if(arg) {
return "Hello!";
} else {
return "Lolo.";
}
}
}
Тут у нас функция fxbool принимает в качестве параметра аргумент типа bool, выясняет true он или false и в зависимости от этого выводит либо Hello! либо Lolo.
На скриншоте я передаю функции положительные числа и они вполне сходят за истину, но когда я передаю ноль, срабатывает секция else.
Числовые типы (int* и uint*)
Буква u перед int означает unsigned т.е. беззнаковое. Например тип int8 может принимать значения от -127 до 128, а uint8 - от 0 до 255.
Что касается * после названия типа - тут интереснее. Вместо звездочки мы ставим цифру, означающую размер этого числового типа от 8 до 256. Например int16, uint32, uint 128, int256.
А uint и int без цифры в суффиксе - это псевдоним uint256 и int256 соответственно.
Можно озадачиться вопросом, на кой это надо.
Точность такая требуется для экономного использования памяти.
Как я уже говорил, в контракте бывают переменные состояния, а бывают локальные в функциях.
Сам Ethereum их рассматривает как storage (хранилища (в блокчейне)) и memory (можно считать живущие в ОЗУ майнеров).
Кстати говоря используя эти ключевые слова при объявлении переменной мы можем изменить способ хранения ее значения.
Например обьявив в функции:
function f() {
uint32 storage num = 0x12;
}
Здесь даже после выхода из функции, значение сохранится в блокчейне и его можно будет считывать в последствии.
Так вот для экономия памяти как первого плана, так и второго, мы и указываем необходимое нам количество памяти. Это позволяет экономить ресурсы и тем самым оптимально настраивать стоимость выполнения контракта.
Приведу следующий пример:
contract TypesTest {
// Эта функция возвращает 2 значения
function pluse(int32 x, int32 y) returns (int64 sum, int64 mul) {
int64 s = x + y;
int64 m = x * y;
return(s, m);
}
}
Как можно заметить функция может возвращать несколько значений.
В примере мы передаем функции два любых целых числа, а она их складывает,затем перемножает, записывая значения в переменные. Затем эти значения функция возвращает.
Адресный тип (address)
По сути это uint160, однако этот тип имеет члены, т.е. свойства и функции, а также предназначен для хранения адресов Ethereum.
Как только вы помещаете в переменную типа address значение адреса аккаунта или контракта, вы соответственно можете пользоваться членами, для работы с этим адресом.
Об этом мы поговорим в следующих уроках. Пока могу вам привести такой пример:
// Адресный тип
// О нем вскоре поговорим подробно
function fxaddr(address addr) returns (string res) {
if(addr != 0x0) {
return "Process...";
} else {
return "Empty address!";
}
}
Здесь мы проверяем, чтобы адрес был не пустым.
Байтовые последовательности и строки (bytes и string)
Тип bytes также имеет суффикс от 1 до 32 т.е. от bytes1 до bytes32 причем у bytes1 есть синоним byte (что вполне логично, т.к. это 1 байт).
Таким образом, если, допустим мы знаем, что наше значение размером 4 байта, то можем поместить его в bytes4, если же это положительное целое значение, то также подойдет uint32.
Определение bytes32[] или меньшее, напоминает по строковою запись в двоичный файл.
Нам же пока больше интересен тип string, т.к он может хранить не просто байты, а много пар байтов в UTF-8. Что собственно и позволяет хранить и отображать наши строковые данные на различных языках.
string str = "Привет, Ethereum!;
function hi() returns (string) {
return str;
}
Приведение типов и автоматическое назначение
Переменные могут "переключаться" на другие типы в случаях когда к переменным применяется оператор подразумневающий изменение типа результата.
Такие неявные преобразования компилятор может проводить, если при этом не теряются данные в преобразуемых переменных. (т.е не уменьшается количество байт для их хранения).
Если неявное преобразование не позволено, то можно выполнить явное. Например:
bytes5 buf = "Hello";
string str = string(buf);
В Solidity существует также автоматическое определение типа.
Это когда вместо названия типа явно, вы пишете ключевое слово var (как в JavaScript), а компилятор по присваиваемому такой переменной значению пытается определить и назначить ей тип.
Понятно, что в этом случае инициализация при объявлении обязательна.
Так же нужно быть аккуратным, так как автоматически назначаемый тип в последствии не меняется, var i = 0; // uint8:
for (var i = 0; i < 3000; i++)
{
// тело цикла
}
Этот цикл окажется бесконечным, т.к. автоматически назначенный тип uint8 не может содержать такое большое значение 3000.
Операция delete
По сути эта операция присваивает начальные значения переменным, массивам и даже структурам.
var n = 1009; // n = 1009
delete n; // n= 0
int8[] arr;
// array init
delete arr; // arr.length = 0
Константы
Переменную можно обьявить константой. В этом случае ее значение нельзя будет менять.
contract ContConst {
uint8 constant x = 105;
bytes5 constant str = "abcdf";
}