Язык 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";
}

В следующем уроке рассмотрим массивы и соответствия


Comments 2


Спасибо! Очень интересный урок! Работающие примеры на solidity найти практически не возможно

14.09.2017 11:01
0