5.solidity的数据结构

一、 主线

次课程主线:练习结构体和数组的使用

训练内容:把用户输入的 newStr 存储到 Struct 中,然后在把结构体存储到数组的永久性存储中;接着需要修改合约,当用户调用 sayHello 时需要传入 id,然后从数组中查找该 id 对应的结构体,如果找到就返回该 id 对应的打招呼短语,否则返回 strVar 作为兜底;

二、solidity 中的数据结构

  • struct: 结构体,把不同的数据类型的数据组成一个结构,可以是基础数据类型可以数据结构或者基础数据类型

  • array: 把相同数据类型的数据组成一个结构

  • mappping: 映射,键值对,对象,以低时间复杂度查找到某一个值

2.1 、结构体

结构体类比 go 里面的 struct,和 TS 中的 interface(或者 type )类似。其作用就是提前约定你这个结构体里面存储了哪些字段,这些字段对应的值类型是什么。

2.1.1 声明

尝试声明一下结构体,使用 struct 关键字。

这个类比 TS 的 interface,又有点像是 class。这是因为后面这个 Info 可以当成构造函数一样调用,调用时传入对应的值,就可以创建实例对象;

但是肯定有类型声明的作用,真正的变量声明还需要等用的时候给变量做类型标识。

声明结构体的格式: struct 结构体名称 { value 的类型 空格 key }

例如我们声明一个结构体,用于保存一次交易调用方发过来的信息:

  • 用户输入的字符串
  • id
  • 交易调用方的地址

代码如下:

go 复制代码
struct Info {
    string phrase;
    uint256 id;
    address addr;
}

2.1.2 实例化

所谓实例化就是创建一个对象,创建的时候按传入对应类型的值即可。

solidity 复制代码
Info memory info = Info(newString, _id, msg.sender); // memory 临时存放

从这段代码可以看出,Info 还发挥了类似构造函数的作用。,

2.2 数组

在 Solidity 中 array 数组是一种数据结构,声明数组要求数组中的各个成员的类型是相同的。这一点和其他强类型语言中的数组保持一致,和 JS 中不同,JS 中的数组对类型不做要求,JS 中的数组更像元组。

2.2.1 声明数组

例如我声明一个数组,用于保存所有交易发生时合约调用方传入的数据。 声明的格式:类型 [] 变量名;

类型的话,可以是自定义的类型例如结构体。

2.2.2 数组创建

数组的创建,和结构体不同,创建和实例化同步完成。例如声明一个存放 Info 类型的数组,代码如下:

go 复制代码
Info [] infos; 

2.2.3 练习

当用户调用 Helloworld 合约时,我们把数据存放到 Info 数组中。当用户调用 sayHello 时传入对应的 id,我们根据 id,从合约中找到他存的数据发送给用户。如果找不到就用 strVar 兜底。

java 复制代码
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;


contract HelloWorld {

    struct Info {
        string phrase;
        uint256 id;
        address addr;
    }
    
    Info[] infos; // 数据默认存储在 storage 中吗?为啥它不用声明存储类型?
    string strVar = 'hello world';

    // 现在需要返回用户输 id 的指定内容
    function sayHello (uint256 _id ) public view returns(string memory) {
        // 遍历数组找到 _id 对应的那个
        for(uint256 i = 0; i < infos.length; i++) {
            if (infos[i].id == _id) {
                return addinfo(infos[i].phrase);
            }
        }

        // Warning: Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. 这个警告是说有些条件没有返回值
        return addinfo(strVar);

    }

  


    // 稍等一下在下面的 Deployed Contracts 中就有 了

    function setHelloWorld(string memory newString, uint256 _id) public {

        // 在这里新的 newString 存储到结构体当中
        
        // 操作一个数据结构的时候需要显式的声明其存储类型

        // address 不需要再单独声明变量(形参),可以从 msg 中获取,msg 升级 solidity 中的环境变量 EnviromentVariable,编译器提供的,无需声明即可访问

        // msg.sender 即谁调用合约的这笔交易是谁发起的:值是个地址

        // msg.value


        // 声明一个 Info 类型的变量需要掉用户 Info() 传入对应的实际值?这些由顺序吗?
        
        Info memory info = Info(newString, _id, msg.sender); // memory 临时存放

        infos.push(info);

    }

  


    function addinfo(string memory helloWorld) internal pure returns(string memory) {
        return string.concat(helloWorld, " from Frank's concat.");
    }

}

在这段代码中有几个小点:

  1. for 循环语句的声明:for (uint256 i = 0; i < infos.length; i++)
  2. address 即交易调用方的地址,可以从 msg 中获取。msg 是 solidity 中的环境变量 EnviromentVariable,编译器提供的,无需声明即可访问。
    • msg.sender 即谁调用合约的这笔交易是谁发起的:值是个地址
    • msg.value

2.3 mapping

mapping 是类似 JS 中的对象或者说叫做哈希表,是一种通过 key 获取对应的值的数据结构。

2.3.1 声明 mapping

mapping 是 key => value 结构,这个语法很像 php 的键值数组声明方式,但是注意,这个只是他的类型声明。

声明 mapping 格式: mapping(key 类型 => value 类型)+空格+变量名;

2.3.2 练习 mapping 使用

训练内容:

思考一个问题:结合上面的 setHello 是把结构体存储到了数组中,在 sayHello 方法中每次采用 for loop 查找数据,当很多人使用这个合约的时候,查找的次数和时间就会越来越多,需要的计算单元也会越来越多,这就导致你的交易越来越贵。

那怎么解决这个问题呢?

为了提高效率,我们使用 mapping 结构存储数据,把 id 当做key,用来查找数据可以一下子找到数据。

mapping 的操作,和 JS 的对象操作一样的:使用 [key] 操作符的方式访问数据,修改则直接对同名 key 赋新值即可。

scss 复制代码
mapping(uint256 => Info) infoBak;

// 赋值
// 完成对 值结构体的 赋值
Info memory info = Info(newString, _id, msg.sender)

// 赋值和修改呢
infoBak[_id] = info;

// 读取
infoBak[_id].phrase 

代码如下:

diff 复制代码
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;


contract HelloWorld {


    struct Info {
        string phrase;
        uint256 id;
        address addr;
    }

-    Info[] infos; // 数组

+    mapping(uint256 => Info) infoMapping; // mapping

    string strVar = 'hello world';

  


+    // 现在需要返回用户输 id 的制定内容分

    function sayHello (uint256 _id ) public view returns(string memory) {
-        // 遍历数组找到 _id 对应的那个
-        for(uint256 i = 0; i < infos.length; i++) {
-            if (infos[i].id == _id) {
-                return addinfo(infos[i].phrase);
-            }
-        }
-        return addinfo(strVar);

+        // infoMapping[_id].phrase 即可渠道 phrase,但是如何判断是否真的找到呢?
+        // 答案:从 infoMapping[_id] 这个结构体中找到最容易为空的那一项,
+         // 判断其是否为空值,这里面选择 address
+        if (infoMapping[_id].addr == address(0x0)) {
+            // 空地址 address(0x0) 16进制的 0
+           return addinfo(strVar);
+        } else {
+            return addinfo(infoMapping[_id].phrase);
+        }
+    }

  


    // 稍等一下在下面的 Deployed Contracts 中就有 了
    function setHelloWorld(string memory newString, uint256 _id) public {

        // 声明一个 Info 类型的变量需要掉用户 Info() 传入对应的实际值?这些由顺序吗?
        Info memory info = Info(newString, _id, msg.sender); // memory 临时存放

        -    info.push(info)

+       // 改为 mapping 存储
+       infoMapping[_id] = info;

    }

    function addinfo(string memory helloWorld) internal pure returns(string memory) {
        return string.concat(helloWorld, " from Frank's concat.");
    }

}

# 三、总结

本章我们学习了 solidity 的数据结构:
1. struct: 结构体
2. array: 同类型的放到一个数组中,使用索引访问数据
3. mapping: 哈希表,以 key => value 的形式存储数据,使用 key 访问数据
相关推荐
掘金-我是哪吒1 小时前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
国服第二切图仔1 小时前
文心开源大模型ERNIE-4.5-0.3B-Paddle私有化部署保姆级教程及技术架构探索
百度·架构·开源·文心大模型·paddle·gitcode
SelectDB2 小时前
SelectDB 在 AWS Graviton ARM 架构下相比 x86 实现 36% 性价比提升
大数据·架构·aws
weixin_437398213 小时前
转Go学习笔记(2)进阶
服务器·笔记·后端·学习·架构·golang
liulilittle4 小时前
SNIProxy 轻量级匿名CDN代理架构与实现
开发语言·网络·c++·网关·架构·cdn·通信
喷火龙8号4 小时前
深入理解MSC架构:现代前后端分离项目的最佳实践
后端·架构
Codebee4 小时前
“自举开发“范式:OneCode如何用低代码重构自身工具链
java·人工智能·架构
掘金-我是哪吒5 小时前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构
JohnYan5 小时前
模板+数据的文档生成技术方案设计和实现
javascript·后端·架构
Da_秀5 小时前
软件工程中耦合度
开发语言·后端·架构·软件工程