I just working with the "Quick-start Guide" in the last part "invoking methods" when i try to call any method always see the error:
Failure [xxxx.testnet]
Error: Contract method is not found
An error occured
Error: Contract method is not found
I have all the methods becase is only copy paste tutorial.
** UPDATE **
I just follow the steps.
Deploy is correct:
near deploy --wasmFile target/wasm32-unknown-unknown/release/rust_counter_tutorial.wasm --accountId josedlujan.testnet
The msg:
Starting deployment. Account id: josedlujan.testnet, node: https://rpc.testnet.near.org, helper: https://helper.testnet.near.org, file: target/wasm32-unknown-unknown/release/rust_counter_tutorial.wasm
Transaction Id 5LiJSuaso4XJ85gYcMopgECgAMvK4BxAHGgfTuGxYHAY
To see the transaction in the transaction explorer, please open this url in your browser
https://explorer.testnet.near.org/transactions/5LiJSuaso4XJ85gYcMopgECgAMvK4BxAHGgfTuGxYHAY
But, when i execute methods.
near call josedlujan.testnet increment --accountId josedlujan.testnet
I always see:
Scheduling a call: josedlujan.testnet.increment()
Receipt: 7GsyvaVGErEr7rXkQdBQSu5F3FRFCQ2BG2LJuDT3t4Gx
Failure [josedlujan.testnet]: Error: Contract method is not found
An error occured
Error: Contract method is not found
But i have the methods lib.rs:
pub fn increment(&mut self) {
// note: adding one like this is an easy way to accidentally overflow
// real smart contracts will want to have safety checks
// e.g. self.val = i8::wrapping_add(self.val, 1);
// https://doc.rust-lang.org/std/primitive.i8.html#method.wrapping_add
self.val += 1;
let log_message = format!("Increased number to {}", self.val);
env::log(log_message.as_bytes());
after_counter_change();
}
/// Decrement (subtract from) the counter.
///
/// In (/src/main.js) this is also added to the "changeMethods" array
/// using near-cli we can call this by:
///
/// ```bash
/// near call counter.YOU.testnet decrement --accountId donation.YOU.testnet
/// ```
pub fn decrement(&mut self) {
// note: subtracting one like this is an easy way to accidentally overflow
// real smart contracts will want to have safety checks
// e.g. self.val = i8::wrapping_sub(self.val, 1);
// https://doc.rust-lang.org/std/primitive.i8.html#method.wrapping_sub
self.val -= 1;
let log_message = format!("Decreased number to {}", self.val);
env::log(log_message.as_bytes());
after_counter_change();
}
lib.rs
//! This contract implements simple counter backed by storage on blockchain.
//!
//! The contract provides methods to [increment] / [decrement] counter and
//! [get it's current value][get_num] or [reset].
//!
//! [increment]: struct.Counter.html#method.increment
//! [decrement]: struct.Counter.html#method.decrement
//! [get_num]: struct.Counter.html#method.get_num
//! [reset]: struct.Counter.html#method.reset
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::{env, near_bindgen};
near_sdk::setup_alloc!();
// add the following attributes to prepare your code for serialization and invocation on the blockchain
// More built-in Rust attributes here: https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
pub struct Counter {
// See more data types at https://doc.rust-lang.org/book/ch03-02-data-types.html
val: i8, // i8 is signed. unsigned integers are also available: u8, u16, u32, u64, u128
}
#[near_bindgen]
impl Counter {
/// Returns 8-bit signed integer of the counter value.
///
/// This must match the type from our struct's 'val' defined above.
///
/// Note, the parameter is `&self` (without being mutable) meaning it doesn't modify state.
/// In the frontend (/src/main.js) this is added to the "viewMethods" array
/// using near-cli we can call this by:
///
/// ```bash
/// near view counter.YOU.testnet get_num
/// ```
pub fn get_num(&self) -> i8 {
return self.val;
}
/// Increment the counter.
///
/// Note, the parameter is "&mut self" as this function modifies state.
/// In the frontend (/src/main.js) this is added to the "changeMethods" array
/// using near-cli we can call this by:
///
/// ```bash
/// near call counter.YOU.testnet increment --accountId donation.YOU.testnet
/// ```
pub fn increment(&mut self) {
// note: adding one like this is an easy way to accidentally overflow
// real smart contracts will want to have safety checks
// e.g. self.val = i8::wrapping_add(self.val, 1);
// https://doc.rust-lang.org/std/primitive.i8.html#method.wrapping_add
self.val += 1;
let log_message = format!("Increased number to {}", self.val);
env::log(log_message.as_bytes());
after_counter_change();
}
/// Decrement (subtract from) the counter.
///
/// In (/src/main.js) this is also added to the "changeMethods" array
/// using near-cli we can call this by:
///
/// ```bash
/// near call counter.YOU.testnet decrement --accountId donation.YOU.testnet
/// ```
pub fn decrement(&mut self) {
// note: subtracting one like this is an easy way to accidentally overflow
// real smart contracts will want to have safety checks
// e.g. self.val = i8::wrapping_sub(self.val, 1);
// https://doc.rust-lang.org/std/primitive.i8.html#method.wrapping_sub
self.val -= 1;
let log_message = format!("Decreased number to {}", self.val);
env::log(log_message.as_bytes());
after_counter_change();
}
/// Reset to zero.
pub fn reset(&mut self) {
self.val = 0;
// Another way to log is to cast a string into bytes, hence "b" below:
env::log(b"Reset counter to zero");
}
}
// unlike the struct's functions above, this function cannot use attributes #[derive(…)] or #[near_bindgen]
// any attempts will throw helpful warnings upon 'cargo build'
// while this function cannot be invoked directly on the blockchain, it can be called from an invoked function
fn after_counter_change() {
// show helpful warning that i8 (8-bit signed integer) will overflow above 127 or below -128
env::log("Make sure you don't overflow, my friend.".as_bytes());
}
/*
* the rest of this file sets up unit tests
* to run these, the command will be:
* cargo test --package rust-counter-tutorial -- --nocapture
* Note: 'rust-counter-tutorial' comes from cargo.toml's 'name' key
*/
// use the attribute below for unit tests
#[cfg(test)]
mod tests {
use super::*;
use near_sdk::MockedBlockchain;
use near_sdk::{testing_env, VMContext};
// part of writing unit tests is setting up a mock context
// in this example, this is only needed for env::log in the contract
// this is also a useful list to peek at when wondering what's available in env::*
fn get_context(input: Vec<u8>, is_view: bool) -> VMContext {
VMContext {
current_account_id: "alice.testnet".to_string(),
signer_account_id: "robert.testnet".to_string(),
signer_account_pk: vec![0, 1, 2],
predecessor_account_id: "jane.testnet".to_string(),
input,
block_index: 0,
block_timestamp: 0,
account_balance: 0,
account_locked_balance: 0,
storage_usage: 0,
attached_deposit: 0,
prepaid_gas: 10u64.pow(18),
random_seed: vec![0, 1, 2],
is_view,
output_data_receivers: vec![],
epoch_height: 19,
}
}
// mark individual unit tests with #[test] for them to be registered and fired
#[test]
fn increment() {
// set up the mock context into the testing environment
let context = get_context(vec![], false);
testing_env!(context);
// instantiate a contract variable with the counter at zero
let mut contract = Counter { val: 0 };
contract.increment();
println!("Value after increment: {}", contract.get_num());
// confirm that we received 1 when calling get_num
assert_eq!(1, contract.get_num());
}
#[test]
fn decrement() {
let context = get_context(vec![], false);
testing_env!(context);
let mut contract = Counter { val: 0 };
contract.decrement();
println!("Value after decrement: {}", contract.get_num());
// confirm that we received -1 when calling get_num
assert_eq!(-1, contract.get_num());
}
#[test]
fn increment_and_reset() {
let context = get_context(vec![], false);
testing_env!(context);
let mut contract = Counter { val: 0 };
contract.increment();
contract.reset();
println!("Value after reset: {}", contract.get_num());
// confirm that we received -1 when calling get_num
assert_eq!(0, contract.get_num());
}
}
```
I assume you are referring to the Quick-start guide for building smart contracts in Rust?
Let's check a few things to make sure you didn't miss any steps.
Did you create a lib.rs file and copy the contents of this smart contract into that file?
Did you successfully deploy the contract using near-cli after you compiled it to WASM?
Are you invoking the methods using near-cli and replacing YOUR_ACCOUNT_HERE with the testnet accountId you just deployed the contract to?
If so, would you be able to share your code / commands so I can help you debug your issue? This tutorial was updated recently (less than a month from today) so it should work, but while I'm waiting for your response I will run through the steps to make sure we don't have a bug on our end. :)
** UPDATE **
Just went through the tutorial and it worked for me. Here was my command using near-cli:
near call ex-1.testnet increment --accountId ex-1.testnet
And the response:
Scheduling a call: ex-1.testnet.increment()
Receipt: 3UTWH43Tgxy1kSSvoax5cRu8A2VEDKZqzgZaEXTwzsBD
Log [ex-1.testnet]: Increased number to 1
Log [ex-1.testnet]: Make sure you don't overflow, my friend.
Transaction Id 2U92KghmCqihUuURVaLGj8x8FrBhQ5z1vE35BVdZG1ob
To see the transaction in the transaction explorer, please open this url in your browser
https://explorer.testnet.near.org/transactions/2U92KghmCqihUuURVaLGj8x8FrBhQ5z1vE35BVdZG1ob
Feel free to reach out on our Discord for further assistance. The proper channel for this discussion would be #near-sdk-rs under #development but #dev-support is ok too. :)
Related
I'm trying to generate a random number in a Near smart contract using Rust. However when I run this code:
pub fn get_last(&self) -> u64 {
let mut rng = rand::thread_rng();
println!("i32: {}, i32: {}", rng.gen::<i32>(), rng.gen::<i32>());
return self.lastval;
}
I've got this error message:
$ near view $ID get_last '{}'
View call: dev-1643558356736-93385541578458.get_last({})
An error occured
Error: Querying [object Object] failed: wasm execution failed with error: FunctionCallError(HostError(GuestPanic { panic_msg: "panicked at 'internal error: entered unreachable code', C:\\Users\\GANSOR-PC\\.cargo\\registry\\src\\github.com-1ecc6299db9ec823\\rand-0.4.6\\src\\jitter.rs:703:9" })).
----------------
jitter.rs:703:9
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
pub fn get_nstime() -> u64 {
unreachable!()
}
in lib.rs I have:
use rand::{thread_rng, Rng};
extern crate rand;
And in Cargo.toml I have:
[dependencies]
near-sdk = "3.1.0"
near-contract-standards = "3.1.1"
rand = "*"
So, where am I wrong?
you can't use a typical random number generator since you are running inside of a virtual machine with no access to typical random seed generators like hardware clock or other machine data
instead consider using the provided random seed you can get through a couple of functions exposed on env like env::random_seed https://github.com/near/near-sdk-rs/blob/master/near-sdk/src/environment/env.rs#L236-L254
/// Returns the random seed from the current block. This 32 byte hash is based on the VRF value from
/// the block. This value is not modified in any way each time this function is called within the
/// same method/block.
pub fn random_seed() -> Vec<u8> {
random_seed_array().to_vec()
}
/// Returns the random seed from the current block. This 32 byte hash is based on the VRF value from
/// the block. This value is not modified in any way each time this function is called within the
/// same method/block.
pub fn random_seed_array() -> [u8; 32] {
//* SAFETY: random_seed syscall will always generate 32 bytes inside of the atomic op register
//* so the read will have a sufficient buffer of 32, and can transmute from uninit
//* because all bytes are filled. This assumes a valid random_seed implementation.
unsafe {
sys::random_seed(ATOMIC_OP_REGISTER);
read_register_fixed_32(ATOMIC_OP_REGISTER)
}
}
here's an example
https://github.com/near-apps/coin-flip/blob/1476bbdf0fee3b6647766ee6e94e40254f728191/contracts/src/lib.rs#L50-L59
pub fn play(&mut self) -> u8 {
let account_id = env::signer_account_id();
let mut credits = self.credits.get(&account_id).unwrap_or(0);
assert!(credits > 0, "no credits to play");
credits = credits - ONE_NEAR;
let rand: u8 = *env::random_seed().get(0).unwrap();
if rand < PROB {
credits = credits + 10 * ONE_NEAR;
}
self.credits.insert(&account_id, &credits);
rand
}
According to the Rust Embedded Book about concurrency, one of the better ways to share some data between contexts is using mutexes with refcells. I understand how they work and why this is necessary. But there is a scenario where the overhead cost seems to much.
The mutex of the cortex_m crate works in this way:
cortex_m::interrupt::free(|cs| {
let my_value = my_mutex.borrow(cs).borrow();
// Do something with the value
});
The mutex requires the cs (CriticalSection) token before it gives access. In a critical section, no interrupts can happen so we know we're the only ones that can change and read the value. This works well.
The scenario I'm in now, however, has the variable be written to once for initialization (at runtime) and then always be treated as a read-only value. In my case it's the clock speed of the MCU. This cannot be a compile-time constant. An example why is waking up from deep sleep: depending on the state of the hardware, it may be chosen to use a slower clock speed to conserve some energy. So at startup (or rather a wakeup where all the RAM is gone) a different clock speed may be selected every time.
If I simply want to read the value, it seems wasteful to go through the whole critical section setup. If this variable may be changed again, then, yes, it's necessary. But that's not the case here. It will only get read.
Is there a better way of reading a shared variable with less overhead and without using unsafe Rust?
With the help of some of the comments, I came up with this:
use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicBool, Ordering};
/// A cell that can be written to once. After that, the cell is readonly and will panic if written to again.
/// Getting the value will panic if it has not already been set. Try 'try_get(_ref)' to see if it has already been set.
///
/// The cell can be used in embedded environments where a variable is initialized once, but later only written to.
/// This can be used in interrupts as well as it implements Sync.
///
/// Usage:
/// ```rust
/// static MY_VAR: DynamicReadOnlyCell<u32> = DynamicReadOnlyCell::new();
///
/// fn main() {
/// initialize();
/// calculate();
/// }
///
/// fn initialize() {
/// // ...
/// MY_VAR.set(42);
/// // ...
/// }
///
/// fn calculate() {
/// let my_var = MY_VAR.get(); // Will be 42
/// // ...
/// }
/// ```
pub struct DynamicReadOnlyCell<T: Sized> {
data: UnsafeCell<Option<T>>,
is_populated: AtomicBool,
}
impl<T: Sized> DynamicReadOnlyCell<T> {
/// Creates a new unpopulated cell
pub const fn new() -> Self {
DynamicReadOnlyCell {
data: UnsafeCell::new(None),
is_populated: AtomicBool::new(false),
}
}
/// Creates a new cell that is already populated
pub const fn from(data: T) -> Self {
DynamicReadOnlyCell {
data: UnsafeCell::new(Some(data)),
is_populated: AtomicBool::new(true),
}
}
/// Populates the cell with data.
/// Panics if the cell is already populated.
pub fn set(&self, data: T) {
cortex_m::interrupt::free(|_| {
if self.is_populated.load(Ordering::Acquire) {
panic!("Trying to set when the cell is already populated");
}
unsafe {
*self.data.get() = Some(data);
}
self.is_populated.store(true, Ordering::Release);
});
}
/// Gets a reference to the data from the cell.
/// Panics if the cell is not yet populated.
#[inline(always)]
pub fn get_ref(&self) -> &T {
if let Some(data) = self.try_get_ref() {
data
} else {
panic!("Trying to get when the cell hasn't been populated yet");
}
}
/// Gets a reference to the data from the cell.
/// Returns Some(T) if the cell is populated.
/// Returns None if the cell is not populated.
#[inline(always)]
pub fn try_get_ref(&self) -> Option<&T> {
if !self.is_populated.load(Ordering::Acquire) {
None
} else {
Some(unsafe { self.data.get().as_ref().unwrap().as_ref().unwrap() })
}
}
}
impl<T: Sized + Copy> DynamicReadOnlyCell<T> {
/// Gets a copy of the data from the cell.
/// Panics if the cell is not yet populated.
#[inline(always)]
pub fn get(&self) -> T {
*self.get_ref()
}
/// Gets a copy of the data from the cell.
/// Returns Some(T) if the cell is populated.
/// Returns None if the cell is not populated.
#[inline(always)]
pub fn try_get(&self) -> Option<T> {
self.try_get_ref().cloned()
}
}
unsafe impl<T: Sized> Sync for DynamicReadOnlyCell<T> {}
I think this is safe due to the atomic check and the critical section in the set. If you spot anything wrong or dodgy, please let me know.
If a &'static will suffice, I would recommend checking out the static_cell crate (Repo, Lib.rs, Docs.rs).
From the README:
use static_cell::StaticCell;
// Statically allocate memory for a `u32`.
static SOME_INT: StaticCell<u32> = StaticCell::new();
// Initialize it at runtime. This returns a `&'static mut`.
let x: &'static mut u32 = SOME_INT.init(42);
assert_eq!(*x, 42);
// Trying to call `.init()` again would panic, because the StaticCell is already initialized.
// SOME_INT.init(42);
I discovered this crate while looking at an implementation of a CYW43439 wifi chip driver. There's a pretty nifty macro you may find useful:
macro_rules! singleton {
($val:expr) => {{
type T = impl Sized;
static STATIC_CELL: StaticCell<T> = StaticCell::new();
STATIC_CELL.init_with(move || $val)
}};
}
// ...
// Init network stack
let stack = &*singleton!(Stack::new(
net_device,
config,
singleton!(StackResources::<1, 2, 8>::new()),
seed
));
This impl is for a struct which is intended to hold a list of cards, but has a maximum number of cards that can be added to it:
trait Property {}
pub struct PropertySet {
max: usize,
properties: Vec<Box<dyn Property>>,
}
impl PropertySet {
// ...
/// This function will panic if a card is added and the set is already full, so
/// you should always check the set size first.
fn add<T: Property>(&mut self, property: T) {
if self.properties.len() + 1 < self.max {
self.properties.push(Box::new(property))
} else {
panic!("The card could not be added to the set; it is full.")
}
}
// ...
}
Panicking seems like an unnecessarily drastic response to the error of trying to add a card to a full set, so I would like to return an Err instead, but that presents a problem because this method moves card. card has to be passed by value because this is the only way I can add it to the Vec.
A common idiom is to return the item as the Err variant of a Result, or embedded in it (like is used in std::sync::Arc::try_unwrap, so you could do something like:
impl PropertySet {
/// This function will panic if a card is added and the set is already full, so
/// you should always check the set size first.
fn add<T: Property>(&mut self, property: T) -> Result<(), T> {
if self.properties.len() + 1 < self.max {
self.properties.push(Box::new(property));
Ok(())
} else {
Err(property)
}
}
}
If you want to adorn the error with more information, you could use a small enum, like:
enum HandError<T: Property> {
HandFull(T),
InsufficientBet,
// ...
}
fn add<T: Property>(&mut self, property: T) -> Result<(), HandError<T>> {
// ...
Err(HandError::HandFull(property))
// ...
}
I would like to use the const C inside the impl R6502 without having to specify the scope R6502::
use bit::BitIndex;
pub struct R6502 {
pub sr: u8, // status register
}
impl R6502 {
// status flag indexs
const C: usize = 0;
const Z: usize = 1;
pub fn step(&mut self) {
self.sr.set_bit(R6502::C, false); // this is what I have to do
self.sr.set_bit(C, false); // this is what I want to do
}
}
I tried use self::C and some other combinations of use to only get errors about items not found.
useing of associated constants is not implemented in Rust 1.20. I haven't found an issue for that, so you can create your own issue in Rust GitHub repository.
In the meantime you can use type alias to reduce character count.
type P = R6502;
self.sr.set_bit(P::C, false);
This is something of a controversial topic, so let me start by explaining my use case, and then talk about the actual problem.
I find that for a bunch of unsafe things, it's important to make sure that you don't leak memory; this is actually quite easy to do if you start using transmute() and forget(). For example, passing a boxed instance to C code for an arbitrary amount of time, then fetching it back out and 'resurrecting it' by using transmute.
Imagine I have a safe wrapper for this sort of API:
trait Foo {}
struct CBox;
impl CBox {
/// Stores value in a bound C api, forget(value)
fn set<T: Foo>(value: T) {
// ...
}
/// Periodically call this and maybe get a callback invoked
fn poll(_: Box<Fn<(EventType, Foo), ()> + Send>) {
// ...
}
}
impl Drop for CBox {
fn drop(&mut self) {
// Safely load all saved Foo's here and discard them, preventing memory leaks
}
}
To test this is actually not leaking any memory, I want some tests like this:
#[cfg(test)]
mod test {
struct IsFoo;
impl Foo for IsFoo {}
impl Drop for IsFoo {
fn drop(&mut self) {
Static::touch();
}
}
#[test]
fn test_drops_actually_work() {
guard = Static::lock(); // Prevent any other use of Static concurrently
Static::reset(); // Set to zero
{
let c = CBox;
c.set(IsFoo);
c.set(IsFoo);
c.poll(/*...*/);
}
assert!(Static::get() == 2); // Assert that all expected drops were invoked
guard.release();
}
}
How can you create this type of static singleton object?
It must use a Semaphore style guard lock to ensure that multiple tests do not concurrently run, and then unsafely access some kind of static mutable value.
I thought perhaps this implementation would work, but practically speaking it fails because occasionally race conditions result in a duplicate execution of init:
/// Global instance
static mut INSTANCE_LOCK: bool = false;
static mut INSTANCE: *mut StaticUtils = 0 as *mut StaticUtils;
static mut WRITE_LOCK: *mut Semaphore = 0 as *mut Semaphore;
static mut LOCK: *mut Semaphore = 0 as *mut Semaphore;
/// Generate instances if they don't exist
unsafe fn init() {
if !INSTANCE_LOCK {
INSTANCE_LOCK = true;
INSTANCE = transmute(box StaticUtils::new());
WRITE_LOCK = transmute(box Semaphore::new(1));
LOCK = transmute(box Semaphore::new(1));
}
}
Note specifically that unlike a normal program where you can be certain that your entry point (main) is always running in a single task, the test runner in Rust does not offer any kind of single entry point like this.
Other, obviously, than specifying the maximum number of tasks; given dozens of tests, only a handful need to do this sort of thing, and it's slow and pointless to limit the test task pool to one just for this one case.
It looks like a use case for std::sync::Once:
use std::sync::{Once, ONCE_INIT};
static INIT: Once = ONCE_INIT;
Then in your tests call
INIT.doit(|| unsafe { init(); });
Once guarantees that your init will only be executed once, no matter how many times you call INIT.doit().
See also lazy_static, which makes things a little more ergonomic. It does essentially the same thing as a static Once for each variable, but wraps it in a type that implements Deref so that you can access it like a normal reference.
Usage looks like this (from the documentation):
#[macro_use]
extern crate lazy_static;
use std::collections::HashMap;
lazy_static! {
static ref HASHMAP: HashMap<u32, &'static str> = {
let mut m = HashMap::new();
m.insert(0, "foo");
m.insert(1, "bar");
m.insert(2, "baz");
m
};
static ref COUNT: usize = HASHMAP.len();
static ref NUMBER: u32 = times_two(21);
}
fn times_two(n: u32) -> u32 { n * 2 }
fn main() {
println!("The map has {} entries.", *COUNT);
println!("The entry for `0` is \"{}\".", HASHMAP.get(&0).unwrap());
println!("A expensive calculation on a static results in: {}.", *NUMBER);
}
Note that autoderef means that you don't even have to use * whenever you call a method on your static variable. The variable will be initialized the first time it's Deref'd.
However, lazy_static variables are immutable (since they're behind a reference). If you want a mutable static, you'll need to use a Mutex:
lazy_static! {
static ref VALUE: Mutex<u64>;
}
impl Drop for IsFoo {
fn drop(&mut self) {
let mut value = VALUE.lock().unwrap();
*value += 1;
}
}
#[test]
fn test_drops_actually_work() {
// Have to drop the mutex guard to unlock, so we put it in its own scope
{
*VALUE.lock().unwrap() = 0;
}
{
let c = CBox;
c.set(IsFoo);
c.set(IsFoo);
c.poll(/*...*/);
}
assert!(*VALUE.lock().unwrap() == 2); // Assert that all expected drops were invoked
}
If you're willing to use nightly Rust you can use SyncLazy instead of the external lazy_static crate:
#![feature(once_cell)]
use std::collections::HashMap;
use std::lazy::SyncLazy;
static HASHMAP: SyncLazy<HashMap<i32, String>> = SyncLazy::new(|| {
println!("initializing");
let mut m = HashMap::new();
m.insert(13, "Spica".to_string());
m.insert(74, "Hoyten".to_string());
m
});
fn main() {
println!("ready");
std::thread::spawn(|| {
println!("{:?}", HASHMAP.get(&13));
}).join().unwrap();
println!("{:?}", HASHMAP.get(&74));
// Prints:
// ready
// initializing
// Some("Spica")
// Some("Hoyten")
}