Tổng quan

Trong ngôn ngữ lập trình Cadence Flow, struct (còn được gọi là “structure”) là một kiểu dữ liệu tùy chỉnh được định nghĩa bởi người dùng. Cấu trúc (structure) cho phép tổ chức các biến khác nhau với các kiểu dữ liệu khác nhau trong cùng một vùng nhớ để tạo thành một đối tượng lớn hơn, phức tạp hơn, có thể dễ dàng truy cập và quản lý.

Struct đặc biệt hữu ích trong việc lưu trữ nhiều giá trị có cấu trúc tương tự với nhau. Trong một struct, các giá trị được gọi là thành phần và mỗi thành phần có một tên và một giá trị. Structs là cấu trúc dữ liệu phức tạp cho phép bạn chứa nhiều giá trị liên quan trong một đối tượng duy nhất.

Ví dụ, bạn có thể tạo một struct cho một nhân viên có tên, số điện thoại và địa chỉ. Mỗi nhân viên sẽ có một giá trị cho từng thành phần của struct, và bạn có thể lưu trữ nhiều nhân viên trong một mảng các struct. Cú pháp tạo một struct sẽ khác nhau tùy theo ngôn ngữ lập trình, nhưng chúng ta thường xác định tên cho struct và các thành phần của nó, sau đó tạo một biến mới với kiểu dữ liệu là struct đó.

Mỗi struct trong Cadence Flow bao gồm một tập hợp các biến, mỗi biến được định nghĩa bởi tên, kiểu dữ liệu và giá trị khởi tạo. Cú pháp để định nghĩa một struct trong Cadence Flow như sau:

struct [tên_struct] { 
    [kiểu_dữ_liệu_1] [tên_biến_1] = [giá_trị_1]; 
    [kiểu_dữ_liệu_2] [tên_biến_2] = [giá_trị_2]; 
    ... 
    [kiểu_dữ_liệu_n] [tên_biến_n] = [giá_trị_n]; 
};

Trong đó, [tên_struct] là tên của cấu trúc, [kiểu_dữ_liệu_i] là kiểu dữ liệu của biến thứ i, [tên_biến_i] là tên của biến thứ i, và [giá_trị_i] là giá trị khởi tạo của biến thứ i (nếu có).

Việc sử dụng struct trong Cadence Flow giúp cho việc quản lý dữ liệu trở nên dễ dàng hơn, đặc biệt là trong các chương trình lớn và phức tạp.

Series hướng dẫn lập trình Cadence

Struct (Cấu trúc)

Dưới đây là ví dục Struct trên ngôn ngữ Cadence

pub struct Profile {
    pub let firstName: String
    pub let lastName: String
    pub let birthday: String
    pub let account: Address

    // `init()` bắt đầu gọi khi Struct được tạo ra...
    // Bạn sẽ phải truyền 4 điều kiện khi tạo struct
    init(_firstName: String, _lastName: String, _birthday: String, _account: Address) {
        self.firstName = _firstName
        self.lastName = _lastName
        self.birthday = _birthday
        self.account = _account
    }
}

Về cơ bản, chúng ta đã xác định một Loại (Type) mới có tên Profile. Nó là một cấu trúc. Như bạn có thể thấy, nó chứa 4 loại dữ liệu:

  1. Tên (firstName)
  2. Họ (lastName)
  3. Ngày sinh (birthday)
  4. Địa chỉ address (account)

Việc tạo một Struct thực sự hữu ích khi chúng ta muốn thông tin được tập hợp lại với nhau trong một nơi.

Hãy suy nghĩ về lý do tại sao điều này là hữu ích. Giả sử chúng ta tạo một tập lệnh mới trong Flow playground và chúng ta muốn trả lại thông tin hồ sơ của ai đó. Chúng ta nên làm việc đó như thế nào? Nếu không có Struct, chúng ta sẽ phải trả về một mảng Chuỗi (String) chứa tất cả thông tin, chuyển đổi account thành String, v.v. Đó là rất nhiều nỗ lực và khó khăn. Thay vào đó, chúng tôi chỉ có thể trả về cấu trúc Hồ sơ. Hãy đi đến một ví dụ thực tế.

Cũng lưu ý rằng Structs có hàm init() được gọi khi Struct được tạo, giống như hàm init() được gọi khi hợp đồng được triển khai. Ngoài ra, bạn sẽ nhận thấy tôi có xu hướng sử dụng “_” trước tên biến của mình trong các hàm init(). Đây chỉ là điều tôi làm để phân biệt giữa biến thực tế và giá trị khởi tạo. Điều này KHÔNG giống với cú pháp nhãn đối số ẩn _ .

Ví dụ thực tế

Hãy bắt đầu bằng cách triển khai Hợp đồng thông minh mới cho tài khoản 0x01:

pub contract Authentication {

    pub var profiles: {Address: Profile}
    
    pub struct Profile {
        pub let firstName: String
        pub let lastName: String
        pub let birthday: String
        pub let account: Address

        init(_firstName: String, _lastName: String, _birthday: String, _account: Address) {
            self.firstName = _firstName
            self.lastName = _lastName
            self.birthday = _birthday
            self.account = _account
        }
    }

    pub fun addProfile(firstName: String, lastName: String, birthday: String, account: Address) {
        let newProfile = Profile(_firstName: firstName, _lastName: lastName, _birthday: birthday, _account: account)
        self.profiles[account] = newProfile
    }

    init() {
        self.profiles = {}
    }

}
  1. Chúng ta đã xác định một hợp đồng mới có tên Authentication
  2. Chúng ta đã xác định một từ điển có tên profiles vơi Address  map với loại là Profile
  3. Chúng ta đã xác định một Struct mới có tên là Profile chứa 4 trường
  4. Chúng ta đã định nghĩa một hàm mới có tên addProfile nhận 4 tham số và tạo Profile mới. Sau đó, nó tạo một mapping mới từ account -> Profile được liên kết với tài khoản đó
  5. Khởi tạo profiles thành một từ điển trống khi hợp đồng được triển khai

Tạo Profile mới

Bây giờ chúng ta đã xác định một Cấu trúc mới, hãy xem tại sao nó có thể hữu ích.

import Authentication from 0x01

transaction() {

    prepare(signer: AuthAccount) {}

    execute {
        log("We're done.")
    }
}

Bây giờ, chúng ta muốn tạo một hồ sơ mới vào profiles trong hợp đồng Authentication. Làm thế nào chúng ta có thể làm điều này? Hãy gọi hàm addProfile với tất cả các đối số mà chúng ta cần như sau: Authentication.addProfile(firstName: firstName, lastName: lastName, birthday: birthday, account: account). Nhưng chờ đã, trước tiên chúng ta cần lấy những đối số này từ đâu đó! Chúng ta có thể làm điều đó bằng cách chuyển chúng vào giao dịch dưới dạng đối số, như sau:

import Authentication from 0x01

transaction(firstName: String, lastName: String, birthday: String, account: Address) {

    prepare(signer: AuthAccount) {}

    execute {
        Authentication.addProfile(firstName: firstName, lastName: lastName, birthday: birthday, account: account)
        log("We're done.")
    }
}

 

Cách đọc Profile

Để đọc Profile mới, hãy mở một Script, sao chép và dán mã tập lệnh soạn sẵn:

import Authentication from 0x01

pub fun main() {

}

Bây giờ, hãy thử đọc Profile. Chúng ta có thể làm điều này bằng cách chuyển vào một Địa chỉ đại diện cho một tài khoản, vì chúng ta đã Mapping các Account -> Profile trong từ điển hồ sơ của chúng ta trong hợp đồng. Sau đó, chúng ta có thể trả lại loại Hồ sơ mà chúng tôi nhận được từ từ điển đó, như vậy:

import Authentication from 0x01

pub fun main(account: Address): Authentication.Profile {
    return Authentication.profiles[account]!
}

Cũng lưu ý kiểu trả về ở đây: Authentication.Profile. Đó là bởi vì chúng tôi đang trả lại một loại Profile được xác định trong hợp đồng Authentication. Các loại luôn dựa trên hợp đồng mà chúng được xác định. Và bùm! Đó là nó. Bây giờ, bất cứ ai gọi tập lệnh này đều có thể có tất cả thông tin hồ sơ họ cần.

Ví dụ khác

Struct
Struct

Cộng đồng hệ sinh thái Flow Việt Nam