contract: test-token
Rust has variables like any other language such as primatives and non-primatives and there are some special features we will disscus them one by one.
transfers_enabled: bool,
here we are defining a variable as a parameter of type 'bool'.
pub struct Contract {
token: FungibleToken,
}
Rust doesn't have classes but instead struct to define a container for data like in these line of code we define a struct with variable 'token'.
Rust is not object oriented language but you can define structs as data container and attach function to operate on this data, also you can do the same for enums.
impl Contract {
here we start to define functions to manipulate the derived type.
pub fn new() -> Self {
Self {
token: FungibleToken::new(b"t".to_vec()),
}
}
inside impl block we can define a static function like this so it can be called from the type itself, here we are returning Self from the function so here we refere to the current type.
static function call
let mut context = VMContextBuilder::new();
here we call a static function 'new' in the type 'VMContextBuilder' and this is widely used method to create a new instance because Rust doesn't have constractor.
pub fn burn(&mut self, account_id: ValidAccountId, amount: U128) {
self.token
.internal_withdraw(account_id.as_ref(), amount.into());
}
inside impl block we can define instance function like this code defines function that takes parameters in addition to '& mut self', rust introduce new concept called mutability so here we borrow mutable refrence for the current object so that the function can edit on it.
fn ft_metadata(&self) -> FungibleTokenMetadata {
unimplemented!()
}
here we define an instance function with '&self' this means the function borrows the current object for readonly like in first line we use it to get data from the current object.
self.token.internal_register_account(account_id.as_ref());
here we are calling an instance function to get data on 'token' variable.
rust has unique concept called ownership and borrowing so when you assign variables to anther so most of the time instead of copying, the ownership taken by the second variable, so we can use borrowing and other things so keep the origin variable.
self.token
.internal_withdraw(account_id.as_ref(), amount.into());
as we mention when we use a variable in passing to function or to assign anther variable so the ownership is transfered and the origin variable move and this exist with non-primative types unless they implement Copy trait, so instead of a copy of the variable passed, the variable will be moved.
Every programming language has tools for effectively handling the duplication of concepts and write extensible agnostic.
trait impl
#[derive(BorshSerialize, BorshDeserialize, PanicOnDefault)]
here we are making automotic implementation for these traits using macro derive and they all we will check them next
tests are rust functions that verify that the non-test code is functioning in the expected manner.
#[test]
fn test_basics() {
so here we are writing a function and anntotate it as test like first line
assert_eq!(contract.ft_balance_of(accounts(0)), 1_000_000.into());
here we are using macro called 'assert_eq' to assert that the right equals left other wise the test will fail
Rust structure it's code in modules so you can have mutliple files in your projects.
use near_contract_standards::fungible_token::FungibleToken;
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::json_types::{ValidAccountId, U128};
use near_sdk::{env, near_bindgen, AccountId, PanicOnDefault, PromiseOrValue};
these lines import struct types from other crates that must be mentioned as a dependence in Cargo.toml.
#[cfg(test)]
mod tests {
we can define new module in rust to structure our code, here we declaring a module at second line ,and at second line we annotate that it is a test module.
Macros are widely used in metaprogramming for generating code at compile time.
#[near_bindgen]
#[derive(BorshSerialize, BorshDeserialize, PanicOnDefault)]
procedure macros allow creating syntax extentions like at first line we use attribute for the struct to generate some code in order to validate near contract, at second line derive-macro to generate implementation for these traits instead of making custom implementation for them.BorshDeserialize, BorshSerialize are used to convert to and from object and binary value but Serialize and Deserialize are used to convert to and from object and json value.
#[init]
here we use attribute the function to define it as an initialization function.
#[cfg(test)]
it a macro to make a compile time switch according a condition and here we make it for testing.
testing_env!(context.build());
macro rules it is a macro like function but evaluated at compile time in addition to have ability to generate codes, this line call macro and as we see it is like function call but have '!' so here we call macro called testing_env macro will initialize the blockchain interface with the VMContext which is either initialized through VMContextBuilder or manually through itself.
assert_eq!(contract.ft_balance_of(accounts(0)), 1_000_000.into());
we are using assert_eq in unit test to assert right equals left other wise the test will fail.