Skip to content

Commit

Permalink
update some docs (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
geometryolife authored Dec 5, 2024
1 parent 46d5cf2 commit 9333008
Show file tree
Hide file tree
Showing 30 changed files with 1,431 additions and 433 deletions.
50 changes: 33 additions & 17 deletions docs/move-basics/address.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
# Address
# 地址类型

Address is a unique identifier of a location on the blockchain. It is used to identify
[packages](./packages.md), [accounts](./what-is-an-account.md), and [objects](./object-storage.md).
Address has a fixed size of 32 bytes and is usually represented as a hexadecimal string prefixed
with `0x`. Addresses are case insensitive.
在Move中,为了表示地址,使用了一种特殊的类型称为address。它是一个32字节的值,用于表示区块链上的任何地址。地址有两种语法形式:以0x为前缀的十六进制地址和命名地址。

```move
0xe51ff5cd221a81c3d6e22b9e670ddf99004d71de4f769b0312b68c7c4872e2f1
// address literal
let value: address = @0x1;
// named address registered in Move.toml
let value = @std;
let other = @rooch;
```

The address above is an example of a valid address. It is 64 characters long (32 bytes) and prefixed
with `0x`.
地址字面量以@符号开头,后面跟着一个十六进制数字或标识符。十六进制数字被解释为一个32字节的值。编译器将在Move.toml文件中查找该标识符,并将其替换为相应的地址。如果在Move.toml文件中找不到该标识符,编译器将抛出错误。

## 转换

Rooch 框架提供了一组辅助函数来处理地址。由于地址类型是一个32字节的值,可以将其转换为u256类型,反之亦然。它还可以转换为vector<u8>类型和从vector<u8>类型转换回地址类型。

示例:将地址转换为u256类型,然后再转换回来。

```move
use rooch::address;
let addr_as_u256: u256 = address::to_u256(@0x1);
let addr = address::from_u256(addr_as_u256);
```

Rooch also has reserved addresses that are used to identify standard packages and objects. Reserved
addresses are typically simple values that are easy to remember and type. For example, the address
of the Standard Library is `0x1`. Addresses, shorter than 32 bytes, are padded with zeros to the
left.
示例:将地址转换为 `vector<u8>` 类型,然后再转换回来。

```move
0x1 = 0x0000000000000000000000000000000000000000000000000000000000000001
use rooch::address;
let addr_as_u8: vector<u8> = address::to_bytes(@0x1);
let addr = address::from_bytes(addr_as_u8);
```

Here are some examples of reserved addresses:
示例:将地址转换为字符串。

- `0x1` - address of the Move Standard Library (alias `std`)
- `0x2` - address of the MoveOS Standard Library (alias `moveos_std`)
- `0x3` - address of the Rooch Framework (alias `rooch_framework`)
```mvoe
use rooch::address;
use std::string::String;
let addr_as_string: String = address::to_string(@0x1);
```
57 changes: 31 additions & 26 deletions docs/move-basics/assert-and-abort.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
# Aborting Execution

<!-- Consider "aborting execution" -->

<!--
Chapter: Basic Syntax
Goal: Introduce abort keyword and `assert!` macro.
Notes:
- previous chapter mentions constants
- error constants standard ECamelCase
- `assert!` macro
- asserts should go before the main logic
- Move has no catch mechanism
- abort codes are local to the module
- there are no error messages emitted
- error codes should handle all possible scenarios in this module
Links:
- constants (previous section)
-->

A transaction can either succeed or fail. Successful execution applies all the changes made to
objects and on-chain data, and the transaction is committed to the blockchain. Alternatively, if a
transaction aborts, the changes are not applied. The `abort` keyword is used to abort a transaction
Expand All @@ -35,6 +15,17 @@ an abort code, which will be returned to the caller of the transaction. The abor
[integer](./primitive-types.md) of type `u64`.

```move
let user_has_access = true;
// abort with a predefined constant if `user_has_access` is false
if (!user_has_access) {
abort 0
};
// there's an alternative syntax using parenthesis`
if (user_has_access) {
abort(1)
};
```

The code above will, of course, abort with abort code `1`.
Expand All @@ -47,6 +38,13 @@ to abort a transaction if a condition is not met. The macro shortens the code ot
an `if` expression + `abort`. The `code` argument is required and has to be a `u64` value.

```move
// aborts if `user_has_access` is `false` with abort code 0
assert!(user_has_access, 0);
// expands into:
if (!user_has_access) {
abort 0
};
```

## Error constants
Expand All @@ -58,10 +56,17 @@ constants and don't have special handling, however, they are used to increase th
code and make it easier to understand the abort scenarios.

```move
/// Error code for when the user has no access.
const ENoAccess: u64 = 0;
/// Trying to access a field that does not exist.
const ENoField: u64 = 1;
/// Updates a record.
public fun update_record(/* ... , */ user_has_access: bool, field_exists: bool) {
// asserts are way more readable now
assert!(user_has_access, ENoAccess);
assert!(field_exists, ENoField);
/* ... */
}
```

## Further reading

- [Abort and Assert](/reference/abort-and-assert.html) in the Move Reference.
- We suggest reading the [Better Error Handling](./../guides/better-error-handling.md) guide to
learn about best practices for error handling in Move.
60 changes: 38 additions & 22 deletions docs/move-basics/constants.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
# Constants

<!--
Chapter: Basic Syntax
Goal: Introduce constants.
Notes:
- constants are immutable
- constants are private
- start with a capital letter always
- stored in the bytecode (but w/o a name)
- mention standard for naming constants
Links:
- next section (abort and assert)
- coding conventions (constants)
- constants (language reference)
-->

Constants are immutable values that are defined at the module level. They often serve as a way to
give names to static values that are used throughout a module. For example, if there's a default
price for a product, you might define a constant for it. Constants are stored in the module's
bytecode, and each time they are used, the value is copied.

```move
module book::shop_price {
use sui::coin::Coin;
use sui::sui::SUI;
/// The price of an item in the shop.
const ITEM_PRICE: u64 = 100;
/// The owner of the shop, an address.
const SHOP_OWNER: address = @0xa11ce;
/// An item sold in the shop.
public struct Item { /* ... */ }
/// Purchase an item from the shop.
public fun purchase(coin: Coin<SUI>): Item {
assert!(coin.value() == ITEM_PRICE, 0);
transfer::public_transfer(coin, SHOP_OWNER);
Item { /* ... */ }
}
}
```

## Naming Convention
Expand All @@ -34,6 +37,11 @@ It's a way to make constants stand out from other identifiers in the code. One e
[error constants](./assert-and-abort.md#assert-and-abort), which are written in ECamelCase.

```move
/// Price of the item used at the shop.
const ITEM_PRICE: u64 = 100;
/// Error constant.
const EItemNotFound: u64 = 1;
```

## Constants are Immutable
Expand All @@ -59,13 +67,21 @@ codebase. But due to constants being private to the module, they can't be access
modules. One way to solve this is to define a "config" module that exports the constants.

```move
module book::config {
const ITEM_PRICE: u64 = 100;
const TAX_RATE: u64 = 10;
const SHIPPING_COST: u64 = 5;
/// Returns the price of an item.
public fun item_price(): u64 { ITEM_PRICE }
/// Returns the tax rate.
public fun tax_rate(): u64 { TAX_RATE }
/// Returns the shipping cost.
public fun shipping_cost(): u64 { SHIPPING_COST }
}
```

This way other modules can import and read the constants, and the update process is simplified. If
the constants need to be changed, only the config module needs to be updated during the package
upgrade.

## Links

- [Constants](/reference/constants.html) in the Move Reference
- [Coding conventions for constants](./../guides/coding-conventions.md#constant)
90 changes: 68 additions & 22 deletions docs/move-basics/control-flow.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,5 @@
# Control Flow

<!--
Chapter: Basic Syntax
Goal: Introduce control flow statements.
Notes:
- if/else is an expression
- while () {} loop
- continue and break
- loop {}
- infinite loop is possible but will lead to gas exhaustion
- return keyword
- if is an expression and as such requires a semicolon (!!!)
Links:
- reference (control flow)
- coding conventions (control flow)
-->

Control flow statements are used to control the flow of execution in a program. They are used to
make decisions, to repeat a block of code, and to exit a block of code early. Move has the following
control flow statements (explained in detail below):
Expand Down Expand Up @@ -47,18 +28,37 @@ it. The `else` keyword is optional, except for the case when the resulting value
variable. We will cover this below.

```move
#[test]
fun test_if() {
let x = 5;
// `x > 0` is a boolean expression.
if (x > 0) {
std::debug::print(&b"X is bigger than 0".to_string())
};
}
```

Let's see how we can use `if` and `else` to assign a value to a variable:

```move
#[test]
fun test_if_else() {
let x = 5;
let y = if (x > 0) {
1
} else {
0
};
assert!(y == 1, 0);
}
```

Here we assign the value of the `if` expression to the variable `y`. If `x` is greater than 0, `y`
will be assigned the value 1, otherwise 0. The `else` block is necessary, because both branches must
return a value of the same type. If we omit the `else` block, the compiler will throw an error.

<!-- TODO: add an error -->

Conditional expressions are one of the most important control flow statements in Move. They can use
either user provided input or some already stored data to make decisions. In particular, they are
Expand Down Expand Up @@ -92,6 +92,30 @@ while (<bool_expression>) { <expressions>; };
Here is an example of a `while` loop with a very simple condition:

```move
// This function iterates over the `x` variable until it reaches 10, the
// return value is the number of iterations it took to reach 10.
//
// If `x` is 0, then the function will return 10.
// If `x` is 5, then the function will return 5.
fun while_loop(mut x: u8): u8 {
let mut y = 0;
// This will loop until `x` is 10.
// And will never run if `x` is 10 or more.
while (x < 10) {
y = y + 1;
x = x + 1;
};
y
}
#[test]
fun test_while() {
assert!(while_loop(0) == 10, 0); // 10 times
assert!(while_loop(5) == 5, 0); // 5 times
assert!(while_loop(10) == 0, 0); // loop never executed
}
```

## Infinite `loop`
Expand All @@ -101,6 +125,18 @@ literally passed `true` to the `while` condition. As you might expect, this woul
loop, and this is almost what the `loop` statement works like.

```move
#[test, expected_failure(out_of_gas, location=Self)]
fun test_infinite_while() {
let mut x = 0;
// This will loop forever.
while (true) {
x = x + 1;
};
// This line will never be executed.
assert!(x == 5, 0);
}
```

An infinite `while`, or `while` without a condition, is a `loop`. The syntax for it is simple:
Expand All @@ -112,10 +148,20 @@ loop { <expressions>; };
Let's rewrite the previous example using `loop` instead of `while`:

```move
#[test, expected_failure(out_of_gas, location=Self)]
fun test_infinite_loop() {
let mut x = 0;
// This will loop forever.
loop {
x = x + 1;
};
// This line will never be executed.
assert!(x == 5, 0);
}
```

<!-- TODO: that's a weak point lmao -->

Infinite loops on their own are not very useful in Move, since every operation in Move costs gas,
and an infinite loop will lead to gas exhaustion. However, they can be used in combination with
`break` and `continue` statements to create more complex loops.
Expand Down
Loading

0 comments on commit 9333008

Please sign in to comment.