当你想要进入Web3领域做开发,可能会想知道应该先学哪门编程语言,或者是哪门语言最适合你。这里有四种现在比较热门的语言:Solidity、Vyper、Move和Rust。下面我会用代码示例解释一下区别,帮你找到学习的方向。
Solidity:这是以太坊智能合约的主要编程语言。如果你想在以太坊上开发去中心化应用(DApps),Solidity几乎是必学的。它有点像JavaScript,但只用于写智能合约。
Vyper:Vyper也是一种用来写智能合约的语言,但它比Solidity更注重安全性,语法上更接近Python。如果你喜欢Python的风格,Vyper可能更适合你。
Move:这门语言是Facebook(现在叫Meta)为了他们的Diem项目(之前叫Libra)设计的。Move的设计目标是提高智能合约的安全性和资源管理能力。如果你对区块链上的资源控制感兴趣,可以考虑学习Move。
Rust:这是一门通用的系统级编程语言,不仅在Web3领域受欢迎,在很多其他领域也很火。Rust的特点是速度快,安全性高,适合做高性能的后端开发。如果你想在Web3领域做一些底层的技术工作,比如构建新的区块链,Rust是个好选择。
以下是具体的示例,更为详细的说明:
Solidity是一种特别的编程语言,它主要是给以太坊这个大平台上的智能合约用的。智能合约就像是自动执行的合同,一旦满足某些条件,就会自动做些事情,比如转账、买卖东西等。Solidity这门语言是由Gavin Wood最先想到的,后来Christian Reitwiessner在2014年把它做出来了。
Solidity就像我们平时用的那些编程语言一样,有函数、字符串处理、类啊、变量啊、做数学计算的功能等等。它跟JavaScript、C++、Python这些语言有点像,但是Solidity是专门给以太坊用的,而且它是图灵完备的,意思就是它能做的事儿非常多,只要你的电脑够强,它就能完成复杂的计算任务。
Solidity的特点有这么几点:
下面是一个用Solidity写的智能合约的例子,简单来说就是一个存钱罐的合约,你可以往里面存以太币,也能取出来:
- // SPDX-License: MIT
- pragma solidity ^0.8.0;
-
- contract PiggyBank {
- // 这个变量用来记录存了多少以太币
- uint public balance;
-
- // 存钱的方法
- function deposit() public payable {
- balance += msg.value;
- }
-
- // 取钱的方法,只能取一次,取完就不能再取了
- function withdraw() public {
- require(balance > 0, "No money to withdraw.");
- payable(msg.sender).transfer(balance);
- balance = 0;
- }
- }
这个合约里,PiggyBank
就是我们的存钱罐,deposit
函数让你可以往里存钱,withdraw
函数则是在有钱的情况下,把所有的钱都取出来
作为第一门可编程的区块链智能合约语言,使用 Solidity 作为区块链编程语言的优势是:
Solidity 更容易学习,并且有更多用于 Solidity 的开发人员工具。
1.容易上手:Solidity这门语言很好学,因为它很像我们常见的JavaScript,所以如果你懂JavaScript,学Solidity就比较轻松,因为它们的语法挺像的。
2.工具多:Solidity有很多好用的工具来帮助开发者,比如OpenZeppelin,这是一个平台,提供了一堆开源的库,能让智能合约更安全。还有Remix这种在线的集成开发环境(IDE)和Hardhat这种本地的开发环境,有了这些工具,开发去中心化应用(DApp)就方便多了。
3.用途广:Solidity不仅在以太坊上用得广,现在连Layer2这种扩展解决方案也在用,所以懂Solidity的开发者找工作时选择更多。
不过,学Solidity之前要小心,因为写智能合约的时候很容易出错,这些错误可能一时半会儿看不出来,所以写完后一定要仔细测试。
2021年,光是加密货币行业就有至少189起安全事故被公开报道,损失了至少76亿美元的加密资产,其中80%的DApp安全事故都是因为智能合约有漏洞。
这些漏洞有的是因为Solidity语言本身的设计有些小缺陷,有的是因为开发者不小心写错了代码。
所以,虽然Solidity好学又好用,但在实际开发中还是要特别注意安全问题。
Rust是由Mozilla的一个员工Graydon Hoare在2006年开始设计的一种编程语言,它的目标是既要有高性能,又要保证安全性,尤其是能在多个任务同时运行时保持安全,这叫做“安全并发”。Rust在写法上有点像C++,但是它在很多方面做了改进,尤其是安全性这块。
Rust的几个主要优点是:
1.静态类型的好处:跟动态语言相比,Rust这样的静态类型语言在编译的时候就能检查出很多错误,比如数据类型不对啊,函数调用错误啊,这样就能在程序运行前就把大部分错误找出来,减少了很多运行时可能出现的麻烦,也使得代码更容易长期维护。
2.让并发编程变得安全:在多线程编程中,如果有两个线程同时操作同一块内存,就可能发生数据竞争,导致程序行为不可预测。Rust的设计在编译阶段就解决了这个问题,保证了即使多个任务同时运行,也不会出现这种安全问题。
3.优秀的内存管理:Rust有一个很牛的设计,就是它不需要垃圾回收器就能保证内存安全。它有一套自己的内存管理规则,确保了程序不会因为内存问题而出错。
下面我给你一个用Rust写的智能合约例子,功能和上面的Solidity存钱罐类似,也就是可以存钱和取钱:
- // 引入标准库中的HashMap,用于存储账户余额
- use std::collections::HashMap;
- // 引入Arc和Mutex,用于原子引用计数(Arc)和互斥锁(Mutex),保证数据的线程安全
- use std::sync::{Arc, Mutex};
-
- // 使用lazy_static宏定义一个静态变量BALANCE,它是一个被Arc和Mutex包裹的HashMap,
- // 键是String类型,值是u64类型,用于存储账户余额。
- lazy_static! {
- static ref BALANCE: Arc<Mutex<HashMap<String, u64>>> = Arc::new(Mutex::new(HashMap::new()));
- }
-
- // 定义一个公共函数deposit,接受一个u64类型的参数amount(存款金额)
- pub fn deposit(amount: u64) {
- // 获取对BALANCE的可变锁,如果锁被成功获取,则执行闭包内的代码
- let mut balances = BALANCE.lock().unwrap();
- // 定义一个字符串常量作为账户余额的键
- let key = "balance".to_string();
- // 使用entry方法插入键值对,如果键不存在则插入默认值0,然后增加存款金额
- *balances.entry(key).or_insert(0) += amount;
- }
-
- // 定义一个公共函数withdraw,接受一个u64类型的参数amount(取款金额)
- pub fn withdraw(amount: u64) -> Result<(), String> {
- // 获取对BALANCE的可变锁,如果锁被成功获取,则执行闭包内的代码
- let mut balances = BALANCE.lock().unwrap();
- // 定义一个字符串常量作为账户余额的键
- let key = "balance".to_string();
- // 尝试获取键对应的值
- if let Some(value) = balances.get_mut(&key) {
- // 检查账户是否有足够的资金进行取款
- if *value >= amount {
- // 减少账户余额
- *value -= amount;
- // 返回Ok(()),表示取款成功
- Ok(())
- } else {
- // 如果余额不足,返回Err,包含错误信息
- Err("Insufficient funds.".to_string())
- }
- } else {
- // 如果找不到账户余额,返回Err,包含错误信息
- Err("Balance not found.".to_string())
- }
- }
这个Rust的代码使用了一个全局的、线程安全的哈希表来存储余额。
deposit
函数用来增加余额,withdraw
函数用来减少余额,如果余额不够,它会返回一个错误。请注意,这只是一个示例,真实的智能合约开发环境会有不同的框架和库,比如使用Rust的智能合约框架如Ink!或CasperLabs的Rust智能合约SDK等。
与 Solidity 相比,Rust 是一种低级(low-level)、多范式的编程语言,速度快且内存效率高——在可扩展性受限的情况下,Rust 大有可为,事实上,在 Web3 中,Rust 的内存安全与高性能对于 DApp 的开发十分友好,目前将 Rust 语言作为核心开发语言的就有 Polkadot、Solana、Near。
1、内存安全
内存安全是某些编程语言中的一个属性,可以防止程序员犯某些类型的内存相关错误。Rust 使用所有权和借用原则实现内存安全。Rust 通过在编译期间消除与内存相关的错误来确保内存安全,这使得它无需像其他内存安全语言那样使用垃圾收集器就可以提高内存效率。
2、速度快、产量高
Rust 能够创建具有高输出和高性能的去中心化程序,这是大规模 DApp 必不可少的功能。Rust 的执行效率更高,因为它可以在不使用垃圾收集器的情况下实现内存安全。
总结
在学习 Rust 之前需要注意的是:虽然 Rust 弥补了 Solidity 的安全与性能缺点,但 Rust 语法更难学习、编写和阅读,其学习难度往往让人望而生畏。
不过在 Rust 语言设计团队(Lang Team)在官方博客中公布的 Rust 语言 2024 年的更新路线图中,就昭示了降低学习难度是 Rust 语言的未来发展方向,Lang Team 力图通过各种手段简化程序,使开发者能更轻松地表达代码意图,而不需要处理逻辑实现的各种细枝末节。
Vyper诞生于2017年,它的设计灵感来源于Python,专门用于编写以太坊上的智能合约。
这种语言的特别之处在于,它能够编译成与Solidity相同的EVM字节码,但它的设计理念更加注重安全性和简洁性。Vyper的开发者们总结了Solidity的一些不足,他们剔除了那些容易引发问题的特性,使得Vyper成为了追求极致安全的开发者的首选。
Vyper的几个关键特性包括:
现在,我将给出一个简单的Vyper智能合约示例,类似于前面提到的存钱罐功能:
- # 版本声明,确保Vyper编译器版本与代码兼容
- # @version 0.3.1
-
- # 定义一个结构体Account,包含一个decimal类型的成员变量balance
- struct Account:
- balance: decimal # 存储每个账户的余额
-
- # 声明一个全局变量accounts,类型为HashMap,键是address类型,值是Account类型
- # 这个变量将在所有函数之间共享,并允许根据地址查找账户信息
- accounts: public(HashMap[address, Account])
-
- # 初始化合约构造函数,当合约第一次部署时调用
- # 使用外部(external)修饰符表示这是一个可以被外部调用的方法
- @external
- def __init__():
- # 当合约部署时,创建者(msg.sender)的地址会自动成为第一个账户
- # 赋予初始余额为0
- self.accounts[msg.sender] = Account({balance: 0})
-
- # 允许外部调用的存款方法
- @external
- def deposit(_amount: decimal):
- # 断言(assert)检查,确保存款金额大于0,否则抛出异常
- assert _amount > 0, "Cannot deposit negative or zero amount"
- # 更新账户余额,增加存款金额
- self.accounts[msg.sender].balance += _amount
-
- # 允许外部调用的取款方法
- @external
- def withdraw(_amount: decimal):
- # 断言检查,确保取款金额大于0
- assert _amount > 0, "Cannot withdraw negative or zero amount"
- # 另一个断言检查,确保账户余额足够覆盖取款金额
- assert self.accounts[msg.sender].balance >= _amount, "Insufficient funds"
- # 更新账户余额,减去取款金额
- self.accounts[msg.sender].balance -= _amount
-
- # 创建一个只读(view)方法,允许外部调用获取余额
- # 方法返回值类型为decimal
- @view
- @external
- def get_balance() -> decimal:
- # 返回调用者(msg.sender)的账户余额
- return self.accounts[msg.sender].balance
这个合约提供了基本的账户管理功能,包括创建账户、存款、取款和查询余额。使用Vyper时,注意所有的函数都必须明确地声明其访问权限(如internal, external等),并且所有存储在区块链上的数据必须显式声明其类型。
此外,Vyper中的断言(assert)语句用于执行合约的安全检查,以防止非法操作或状态。
市场占有率低:Vyper 并不是目前主流的选择。举个例子,当你在 Github 这个全球最大的开源代码仓库平台上搜索时,你会发现使用 Vyper 编写的智能合约数量远远少于使用 Solidity 的。这意味着 Solidity 在智能合约开发领域有更广泛的使用和更大的社区支持。
发展阶段:Vyper 目前仍处于不断改进和完善的过程中。虽然它的设计初衷是为了提供比 Solidity 更加安全的智能合约编写方式,但它的成熟度和经过实战验证的安全性还需要时间来证明。对于追求稳定性和可靠性的开发者或者项目来说,这可能是一个需要考虑的因素。
Move是一种最近非常流行的编程语言,它实际上是基于Rust语言的一些设计理念而创造出来的,最早在2019年由Meta公司(当时还叫Facebook)为了他们的Diem区块链项目开发。
后来,虽然Diem项目没有继续下去,但Move语言的团队成员出去创立了Aptos和Sui这两个区块链平台,并且继续使用Move作为它们的核心编程语言。
1.面向资产编程:Move从一开始就被设计成让“资源”成为程序中的重要组成部分。这里的资源指的是任何有价值的东西,比如加密货币或者NFT。在Move中,资源不能被随意复制或者丢失,它们只能从一个地方“移动”到另一个地方。这就意味着在Move中,每一个资源都天然具备稀缺性和访问控制,确保了资源的唯一性和安全性。
2.安全性:Move继承了Rust语言的许多安全特性,这使得它在设计上就倾向于防止一些常见的编程错误。除此之外,Move团队还开发了形式化验证器Move Prover和字节码验证器bytecode verifier,这些工具可以在智能合约部署前检测和防止潜在的安全漏洞,从而增强合约的安全性。
3.模块化:在Move中,智能合约可以被设计成模块化的组件,这意味着开发者可以重用和更新代码片段而不影响整个系统的稳定性。与Solidity不同,Solidity中的数据通常只能在单一合约内修改,而Move允许跨合约的数据操作,提高了代码的灵活性和可维护性。
现在,我将给出一个简单的Move智能合约示例,类似于前面提到的存钱罐功能:
- // 定义一个名为 BalanceManager 的模块
- module BalanceManager {
-
- // 定义一个名为 Balance 的结构体,它包含一个无符号64位整数 value,这个结构体将被标记为 key,
- // 这意味着它可以被存储在全球存储中,并可以通过一个账户地址直接访问。
- struct Balance has key {
- value: u64,
- }
-
- // 定义一个存款函数,接受一个签名者引用(代表账户所有者)和一个要存入的金额
- fun deposit(account: &signer, amount: u64) {
- // 借用全局存储中的 Balance 结构体实例,这需要一个账户地址
- let mut balance = Balance::borrow_global<Balance>(account);
- // 将传入的金额加到余额上
- balance.value += amount;
- }
-
- // 定义一个取款函数,接受一个签名者引用和一个要取出的金额
- fun withdraw(account: &signer, amount: u64): Option
{ - // 借用全局存储中的 Balance 结构体实例,并获取可变引用,以便修改余额
- let mut balance = Balance::borrow_global_mut<Balance>(account);
- // 检查余额是否足够支付取款金额
- if balance.value >= amount {
- // 如果余额足够,则从余额中减去取款金额并返回一个 Some 包含取款金额的 Option
- balance.value -= amount;
- Some(amount)
- } else {
- // 如果余额不足,则返回 None 表示取款失败
- None
- }
- }
-
- // 定义一个创建账户的函数,接受一个签名者引用
- fun create_account(account: &signer) {
- // 在全局存储中为该账户创建一个新的空 Balance 实例
- Balance::new_empty(account);
- }
- }
这个示例展示了如何在Move中使用模块、结构体、函数以及全局存储的概念。
在Move中,全局存储是所有账户和智能合约可以访问的一个共享数据库,它用于存储所有状态信息,如用户余额。签名者引用(&signer
)通常用于指明哪个账户正在执行操作,这是Move中资源管理和权限控制的一部分。
实际的Move代码可能需要包括更多的细节,例如类型检查、资源管理以及与特定区块链平台的交互细节。
此外,上述代码中的borrow_global
和borrow_global_mut
函数以及new_empty
方法是假设已经存在并用于与全局存储交互的标准库函数,具体实现可能会因不同的Move框架和标准库版本而异。
在考虑学习Move语言时,得明白它还是个新手,就像刚学会走路的小孩,还没准备好跑马拉松。Move在Aptos和Sui这样的区块链里玩得挺欢,但说到广泛使用,它还得长大一些,证明自己既安全又可靠。
而且,Move的世界还在建规矩,合约怎么写,生态怎么搭,都还在摸索阶段。如果你是个想在多条链上做应用的大佬,Move的特殊性可能会让你头大。
现在,Solidity就像是区块链界的元老,它在以太坊和其他链上混得风生水起,市场对懂它的程序员需求超级高。
所以,Solidity成了很多人的区块链敲门砖。
简而言之,Solidity现在最火,Rust潜力无限,Move还在成长,Vyper有待观察。
选哪门语言,看个人喜好和市场需求,多技能傍身,职场路更宽。