Monday, April 10, 2023

Unit testing with SqlException

Source:

https://blog.jonathanchannon.com/2014-01-02-unit-testing-with-sqlexception/

 

private SqlException GetSqlException()

{

    SqlErrorCollection collection = Construct<SqlErrorCollection>();

    SqlError error = Construct<SqlError>(-2, (byte)2, (byte)3, "server name", "error message", "proc", 100, (uint)1);


    typeof(SqlErrorCollection)

        .GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance)

        .Invoke(collection, new object[] { error });    

    

    var e = typeof(SqlException)

        .GetMethod("CreateException", BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.ExplicitThis, new[] { typeof(SqlErrorCollection), typeof(string) }, new ParameterModifier[] { })

        .Invoke(null, new object[] { collection, "11.0.0" }) as SqlException;


    return e;

}


private T Construct<T>(params object[] p)

{

    return (T)typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0].Invoke(p);

}

Monday, April 25, 2022

Go vs Rust - Unsafe

Go

If you have a background of C language, you must know what pointer means and its usage. With pointer, you are free to operate any data at memory level which means you have great power, but this means that you have great responsibility as well. That's why it might be considered unsafe in lots of cases.

package main
import "fmt"
func double(x *int) {
    *x += *x
    x = nil
}
func main() {
    var a = 3
    double(&a)
    fmt.Println(a) // 6
    p := &a
    double(p)
    fmt.Println(a, p == nil) // 12 false
}

The major restrictions of pointer in GoLang are:

  • No mathematic operations can be performed on pointer
  • Cannot convert between different types of pointer
  • Different types of pointer cannot be compared with == or !=
  • Cannot assign one type of pointer to another type of pointer

GoLang Unsafe

There is also type unsafe pointer, it is unsafe.Pointer residing in unsafe package.

In unsafe package, there is a Pointer defined.

type ArbitraryType int
type Pointer *ArbitraryType

This is similar to void* in C. Also three additional functions are defined.

func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr

Sizeof returned the number of bytes x takes, it doesn't count the actual number of bytes its content takes. 

Offsetof returns the position where one member of a struct within the struct away from the beginning position of the struct

Alignof returns m which means the number of bytes which can be divided when align the memory in the struct.

unsafe package provide two important features:

  • Any pointer can be converted to unsafe.Pointer and vice versa
  • uintptr can be converted to unsafe.Pointer and vice versa

Get or update value of unexported property in struct

package main
import (
    "fmt"
    "unsafe"
)
type Programmer struct {
    name string
    language string
}
func main() {
    p := Programmer{"stefno", "go"}
    fmt.Println(p)
    name := (*string)(unsafe.Pointer(&p))
    *name = "qcrao"
    lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.language)))
    *lang = "Golang"
    fmt.Println(p)
}


Rust

Unsafe Superpowers

To switch to unsafe Rust, use the unsafe keyword and then start a new block that holds the unsafe code. You can take five actions in unsafe Rust, called unsafe superpowers, that you can’t in safe Rust. Those superpowers include the ability to:

  • Dereference a raw pointer
  • Call an unsafe function or method
  • Access or modify a mutable static variable
  • Implement an unsafe trait
  • Access fields of unions

Dereferencing a Raw Pointer

Different from references and smart pointers, raw pointers:

Are allowed to ignore the borrowing rules by having both immutable and mutable pointers or multiple mutable pointers to the same location
Aren’t guaranteed to point to valid memory
Are allowed to be null
Don’t implement any automatic cleanup

we can create raw pointers in safe code, but dereference raw pointers and read the data being pointed to that requires an unsafe block.

    let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
    unsafe {
        println!("r1 is: {}", *r1);
        println!("r2 is: {}", *r2);
    }

Calling an Unsafe Function or Method

    unsafe fn dangerous() {}
    unsafe {
        dangerous();
    }

Creating a Safe Abstraction over Unsafe Code

use std::slice;
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    let ptr = slice.as_mut_ptr();
    assert!(mid <= len);
    unsafe {
        (
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

Using extern Functions to Call External Code

Sometimes, your Rust code might need to interact with code written in another language. For this, Rust has a keyword, extern, that facilitates the creation and use of a Foreign Function Interface (FFI).

extern "C" {
    fn abs(input: i32) -> i32;
}
fn main() {
    unsafe {
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

We can also use extern to create an interface that allows other languages to call Rust functions. Instead of an extern block, we add the extern keyword and specify the application binary interface (ABI) to use just before the fn keyword. We also need to add a #[no_mangle] annotation to tell the Rust compiler not to mangle the name of this function. Mangling is when a compiler changes the name we’ve given a function to a different name that contains more information for other parts of the compilation process to consume but is less human readable. 

#[no_mangle]
pub extern "C" fn call_from_c() {
    println!("Just called a Rust function from C!");
}

Accessing or Modifying a Mutable Static Variable

Static variables are similar to constants.  Static variables can only store references with the 'static lifetime, which means the Rust compiler can figure out the lifetime and we aren’t required to annotate it explicitly. Accessing an immutable static variable is safe.

Constants and immutable static variables might seem similar, but a subtle difference is that values in a static variable have a fixed address in memory. Using the value will always access the same data. Constants, on the other hand, are allowed to duplicate their data whenever they’re used.

Another difference between constants and static variables is that static variables can be mutable. Accessing and modifying mutable static variables is unsafe. 

static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}
fn main() {
    add_to_count(3);
    unsafe {
        println!("COUNTER: {}", COUNTER);
    }
}

Implementing an Unsafe Trait

Another use case for unsafe is implementing an unsafe trait. A trait is unsafe when at least one of its methods has some invariant that the compiler can’t verify. We can declare that a trait is unsafe by adding the unsafe keyword before trait and marking the implementation of the trait as unsafe too.

unsafe trait Foo {
    // methods go here
}
unsafe impl Foo for i32 {
    // method implementations go here
}

Accessing Fields of a Union

The final action that works only with unsafe is accessing fields of a union. A union is similar to a struct, but only one declared field is used in a particular instance at one time. Unions are primarily used to interface with unions in C code. Accessing union fields is unsafe because Rust can’t guarantee the type of the data currently being stored in the union instance

Go vs Rust - Error Handling

 1 Go

Error handling in Go is a little different than other mainstream programming languages like Java, JavaScript, or Python. Go’s built-in errors don’t contain stack traces, nor do they support conventional try/catch methods to handle them. Instead, errors in Go are just values returned by functions, and they can be treated in much the same way as any other datatype - leading to a surprisingly lightweight and simple design.

The Error Type

The error type in Go is implemented as the following interface:

type error interface {
    Error() string
}

So basically, an error is anything that implements the Error() method, which returns an error message as a string. It’s that simple!

Constructing Errors

Errors can be constructed on the fly using Go’s built-in errors or fmt packages. For example, the following function uses the errors package to return a new error with a static error message:

package main
import "errors"
func DoSomething() error {
    return errors.New("something didn't work")
}

Similarly, the fmt package can be used to add dynamic data to the error, such as an int, string, or another error. For example:

package main
import "fmt"
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("can't divide '%d' by zero", a)
    }
    return a / b, nil
}

Note that fmt.Errorf will prove extremely useful when used to wrap another error with the %w format verb.

There are a few other important things to note in the example above.

  • Errors can be returned as nil, and in fact, it’s the default, or “zero”, value of on error in Go. This is important since checking if err != nil is the idiomatic way to determine if an error was encountered 
  • Errors are typically returned as the last argument in a function. Hence in our example above, we return an int and an error, in that order.
  • When we do return an error, the other arguments returned by the function are typically returned as their default “zero” value. A user of a function may expect that if a non-nil error is returned, then the other arguments returned are not relevant.
  • Lastly, error messages are usually written in lower-case and don’t end in punctuation. Exceptions can be made though, for example when including a proper noun, a function name that begins with a capital letter, etc.

Defining Expected Errors

Another important technique in Go is defining expected Errors so they can be checked for explicitly in other parts of the code. This becomes useful when you need to execute a different branch of code if a certain kind of error is encountered.

Defining Sentinel Errors

Building on the Divide function from earlier, we can improve the error signaling by pre-defining a “Sentinel” error. Calling functions can explicitly check for this error using errors.Is:

package main
import (
    "errors"
    "fmt"
)
var ErrDivideByZero = errors.New("divide by zero")
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, ErrDivideByZero
    }
    return a / b, nil
}
func main() {
    a, b := 10, 0
    result, err := Divide(a, b)
    if err != nil {
        switch {
        case errors.Is(err, ErrDivideByZero):
            fmt.Println("divide by zero error")
        default:
            fmt.Printf("unexpected division error: %s\n", err)
        }
        return
    }
    fmt.Printf("%d / %d = %d\n", a, b, result)
}

Defining Custom Error Types

There can be times when you might want a little more functionality. Perhaps you want an error to carry additional data fields, or maybe the error’s message should populate itself with dynamic values when it’s printed.

Below is a slight rework of the previous example. Notice the new type DivisionError, which implements the Error interface. We can make use of errors.As to check and convert from a standard error to our more specific DivisionError.

package main
import (
    "errors"
    "fmt"
)
type DivisionError struct {
    IntA int
    IntB int
    Msg  string
}
func (e *DivisionError) Error() string { 
    return e.Msg
}
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, &DivisionError{
            Msg: fmt.Sprintf("cannot divide '%d' by zero", a),
            IntA: a, IntB: b,
        }
    }
    return a / b, nil
}
func main() {
    a, b := 10, 0
    result, err := Divide(a, b)
    if err != nil {
        var divErr *DivisionError
        switch {
        case errors.As(err, &divErr):
            fmt.Printf("%d / %d is not mathematically valid: %s\n",
              divErr.IntA, divErr.IntB, divErr.Error())
        default:
            fmt.Printf("unexpected division error: %s\n", err)
        }
        return
    }
    fmt.Printf("%d / %d = %d\n", a, b, result)
}

Wrapping Errors

Often in real-world programs, there can be many more functions involved - from the function where the error is produced, to where it is eventually handled, and any number of additional functions in-between.

Uses fmt.Errorf with a %w verb to “wrap” errors as they “bubble up” through the other function calls.

func FindUser(username string) (*db.User, error) {
    u, err := db.Find(username)
    if err != nil {
        return nil, fmt.Errorf("FindUser: failed executing db query: %w", err)
    }
    return u, nil
}
func SetUserAge(u *db.User, age int) error {
    if err := db.SetAge(u, age); err != nil {
      return fmt.Errorf("SetUserAge: failed executing db update: %w", err)
    }
}
func FindAndSetUserAge(username string, age int) error {
  var user *User
  var err error
  user, err = FindUser(username)
  if err != nil {
      return fmt.Errorf("FindAndSetUserAge: %w", err)
  }
  if err = SetUserAge(user, age); err != nil {
      return fmt.Errorf("FindAndSetUserAge: %w", err)
  }
  return nil
}
func main() {
    if err := FindAndSetUserAge("bob@example.com", 21); err != nil {
        fmt.Println("failed finding or updating user: %s", err)
        return
    }
    fmt.Println("successfully updated user's age")
}

If used correctly, error wrapping can provide additional context about the lineage of an error, in ways similar to a traditional stack-trace.

Wrapping also preserves the original error, which means errors.Is and errors.As continue to work, regardless of how many times an error has been wrapped. We can also call errors.Unwrap to return the previous error in the chain.

In summary, here’s the gist of what was covered here:

  • Errors in Go are just lightweight pieces of data that implement the Error interface
  • Predefined errors will improve signaling, allowing us to check which error occurred
  • Wrap errors to add enough context to trace through function calls (similar to a stack trace)
2 Rust

Rust groups errors into two major categories: recoverable and unrecoverable errors.Rust doesn’t have exceptions. Instead, it has the type Result<T, E> for recoverable errors and the panic! macro that stops execution when the program encounters an unrecoverable error. 

Panic Macro and Unrecoverable Errors

panic! macro allows a program to terminate immediately and provide feedback to the caller of the program. It should be used when a program reaches an unrecoverable state.

fn main() {
   panic!("Hello");
   println!("End of main"); //unreachable statement
}

Unwrap

Expects self to be Ok/Some and returns the value contained within. If it is Err or None instead, it raises a panic with the contents of the error displayed.

fn main(){
   let result = is_even(10).unwrap();
   println!("result is {}",result);
}
fn is_even(no:i32)->Result<bool,String> {
   if no % 2 == 0 {
      return Ok(true);
   } else {
      return Err("NOT_AN_EVEN".to_string());
   }
}

or

use std::fs::File;
use std::io::ErrorKind;
fn main() {
    let f = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

Expect

Behaves like unwrap, except that it outputs a custom message before panicking in addition to the contents of the error.

use std::fs::File;
fn main(){
   let f = File::open("pqr.txt").expect("File not able to open");
   //file does not exist
   println!("end of main");
}

Propagating Errors
When a function’s implementation calls something that might fail, instead of handling the error within the function itself, you can return the error to the calling code so that it can decide what to do.

Result Enum and Recoverable Errors

Enum Result – <T,E> can be used to handle recoverable errors. It has two variants − OK and Err. T and E are generic type parameters. T represents the type of the value that will be returned in a success case within the OK variant, and E represents the type of the error that will be returned in a failure case within the Err variant.

enum Result<T,E> {
   OK(T),
   Err(E)
}

use std::fs::File;
fn main() {
   let f = File::open("main.jpg");   // main.jpg doesn't exist
   match f {
      Ok(f)=> {
         println!("file found {:?}",f);
      },
      Err(e)=> {
         println!("file not found \n{:?}",e);   //handled error
      }
   }
   println!("end of main");
}

Aliases for Result

type Result<T> = Result<T, std::io::Error>;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

A Shortcut for Propagating Errors: the ? Operator

use std::fs::File;
use std::io;
use std::io::Read;
fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

Defining an error type

Sometimes it simplifies the code to mask all of the different errors with a single type of error.

type Result<T> = std::result::Result<T, DoubleError>;
#[derive(Debug)]
enum DoubleError {
    EmptyVec,
    // We will defer to the parse error implementation for their error.
    // Supplying extra info requires adding more data to the type.
    Parse(ParseIntError),
}
impl fmt::Display for DoubleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DoubleError::EmptyVec =>
                write!(f, "please use a vector with at least one element"),
            // The wrapped error contains additional information and is available
            // via the source() method.
            DoubleError::Parse(..) =>
                write!(f, "the provided string could not be parsed as int"),
        }
    }
}
impl error::Error for DoubleError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            DoubleError::EmptyVec => None,
            // The cause is the underlying implementation error type. Is implicitly
            // cast to the trait object `&error::Error`. This works because the
            // underlying type already implements the `Error` trait.
            DoubleError::Parse(ref e) => Some(e),
        }
    }
}






Sunday, April 24, 2022

Go vs Rust - Macros in Rust

 Macros

Fundamentally, macros are a way of writing code that writes other code, which is known as metaprogramming.

The term macro refers to a family of features in Rust: declarative macros with macro_rules! and three kinds of procedural macros:

  • Custom #[derive] macros that specify code added with the derive attribute used on structs and enums
  • Attribute-like macros that define custom attributes usable on any item
  • Function-like macros that look like function calls but operate on the tokens specified as their argument
The Difference Between Macros and Functions
  • macros are expanded before the compiler interprets the meaning of the code, so a macro can, for example, implement a trait on a given type. A function can’t, because it gets called at runtime and a trait needs to be implemented at compile time.
  • because you’re writing Rust code that writes Rust code. macro definitions are generally more difficult to read, understand, and maintain than function definitions.
  •  must define macros or bring them into scope before you call them in a file, as opposed to functions you can define anywhere and call anywhere.
The most widely used form of macros in Rust is declarative macros. These are also sometimes referred to as “macros by example,” “macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression. 

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

The #[macro_export] annotation indicates that this macro should be made available whenever the crate in which the macro is defined is brought into scope. Without this annotation, the macro can’t be brought into scope.

We then start the macro definition with macro_rules! and the name of the macro we’re defining without the exclamation mark.

We have one arm with the pattern ( $( $x:expr ),* ), followed by => and the block of code associated with this pattern. If the pattern matches, the associated block of code will be emitted. 

First, a set of parentheses encompasses the whole pattern. A dollar sign ($) is next, followed by a set of parentheses that captures values that match the pattern within the parentheses for use in the replacement code. Within $() is $x:expr, which matches any Rust expression and gives the expression the name $x.

The comma following $() indicates that a literal comma separator character could optionally appear after the code that matches the code in $(). The * specifies that the pattern matches zero or more of whatever precedes the *.

When we call this macro with vec![1, 2, 3];, the $x pattern matches three times with the three expressions 1, 2, and 3.

{
    let mut temp_vec = Vec::new();
    temp_vec.push(1);
    temp_vec.push(2);
    temp_vec.push(3);
    temp_vec
}

Procedural Macros for Generating Code from Attributes

Procedural macros act more like functions (and are a type of procedure). Procedural macros accept some code as an input, operate on that code, and produce some code as an output.

When creating procedural macros, the definitions must reside in their own crate with a special crate type. We can have multiple kinds of procedural macros in the same crate.

We’ll need functionality from the syn and quote crates, as you’ll see in a moment, so we need to add them as dependencies. Add the following to the Cargo.toml file for hello_macro_derive:

Filename: hello_macro_derive/Cargo.toml

[lib]
proc-macro = true

[dependencies]
syn = "1.0"
quote = "1.0"

Filename: hello_macro_derive/src/lib.rs

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();
    // Build the trait implementation
    impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    gen.into()
}

Attribute-like Macros

Attribute-like macros are similar to custom derive macros, but instead of generating code for the derive attribute, they allow you to create new attributes. They’re also more flexible: derive only works for structs and enums; attributes can be applied to other items as well, such as functions.

#[route(GET, "/")]
fn index() {

The signature of the macro definition function would look like this:

#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {

Here, we have two parameters of type TokenStream. The first is for the contents of the attribute: the GET, "/" part. The second is the body of the item the attribute is attached to: in this case, fn index() {} and the rest of the function’s body.

Function-like Macros

Function-like macros define macros that look like function calls. Similarly to macro_rules! macros, they’re more flexible than functions; for example, they can take an unknown number of arguments. However, macro_rules! macros can be defined only using the match-like syntax. Function-like macros take a TokenStream parameter and their definition manipulates that TokenStream using Rust code as the other two types of procedural macros do.

An example of a function-like macro is an sql! macro that might be called like so:

let sql = sql!(SELECT * FROM posts WHERE id=1);

The sql! macro would be defined like this:

#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {




Saturday, April 23, 2022

Go vs Rust - Concurrency

 Go

Go has rich support for concurrency using goroutines and channels. 

Goroutines and Channels are a lightweight built-in feature for managing concurrency and communication between several functions executing at the same time. 

Goroutines

A goroutine is a function that is capable of running concurrently with other functions. To create a goroutine we use the keyword go followed by a function invocation:

package main
import "fmt"
func f(n int) {
  for i := 0; i < 10; i++ {
    fmt.Println(n, ":", i)
  }
}
func main() {
  go f(0)
  var input string
  fmt.Scanln(&input)
}

Channels

Channels provide a way for two goroutines to communicate with one another and synchronize their execution. Here is an example program using channels:

package main
import (
  "fmt"
  "time"
)
func pinger(c chan string) {
  for i := 0; ; i++ {
    c <- "ping"
  }
}
func printer(c chan string) {
  for {
    msg := <- c
    fmt.Println(msg)
    time.Sleep(time.Second * 1)
  }
}
func main() {
  var c chan string = make(chan string)
  go pinger(c)
  go printer(c)
  var input string
  fmt.Scanln(&input)
}

Channel Direction

We can specify a direction on a channel type thus restricting it to either sending or receiving. For example pinger's function signature can be changed to this:

func pinger(c chan<- string)

Now c can only be sent to. Attempting to receive from c will result in a compiler error. Similarly we can change printer to this:

func printer(c <-chan string)

A channel that doesn't have these restrictions is known as bi-directional. A bi-directional channel can be passed to a function that takes send-only or receive-only channels, but the reverse is not true.

Select

Go has a special statement called select which works like a switch but for channels:

The select statement is often used to implement a timeout. 

The default case happens immediately if none of the channels are ready.

select {
case msg1 := <- c1:
  fmt.Println("Message 1", msg1)
case msg2 := <- c2:
  fmt.Println("Message 2", msg2)
case <- time.After(time.Second):
  fmt.Println("timeout")
default:
  fmt.Println("nothing ready")
}

Buffered Channels

It's also possible to pass a second parameter to the make function when creating a channel:

c := make(chan int, 1)

This creates a buffered channel with a capacity of 1. Normally channels are synchronous; both sides of the channel will wait until the other side is ready. A buffered channel is asynchronous; sending or receiving a message will not wait unless the channel is already full.

Anonymous functions as goroutines

func main() {
fmt.Println("We are executing a goroutine")
arr := []int{2,3,4}
ch := make(chan int, len(arr))
go func(arr []int, ch chan int) {
for _, elem := range arr {
ch <- elem * 3
}
}(arr, ch)
for i := 0; i < len(arr); i++ {
fmt.Printf("Result: %v \n", <- ch)
}
}

Mutual exclusion

A problem that may arise when working with concurrency is when share the same resources, which shouldn’t be accessed at the same time by multiple goroutines.

In concurrency, the block of code that modifies shared resources is called the critical section.

package main
import (
"fmt"
"time"
)
var n = 1
var mu sync.Mutex
func timesThree() {
  mu.Lock()
defer mu.Unlock()
n *= 3
fmt.Println(n)
}
func main() {
fmt.Println("We are executing a goroutine")
for i := 0; i < 10; i++ {
go timesThree()
}
time.Sleep(time.Second)
}

Rust

Rust's memory safety features also apply to its concurrency story. Even concurrent Rust programs must be memory safe, having no data races. Rust's type system is up to the task, and gives you powerful ways to reason about concurrent code at compile time.

Send

The first trait we're going to talk about is Send. When a type T implements Send, it indicates that something of this type is able to have ownership transferred safely between threads.

This is important to enforce certain restrictions. For example, if we have a channel connecting two threads, we would want to be able to send some data down the channel and to the other thread. Therefore, we'd ensure that Send was implemented for that type.

Sync

The second of these traits is called Sync. When a type T implements Sync, it indicates that something of this type has no possibility of introducing memory unsafety when used from multiple threads concurrently through shared references. This implies that types which don't have interior mutability are inherently Sync, which includes simple primitive types (like u8) and aggregate types containing them.

For sharing references across threads, Rust provides a wrapper type called Arc<T>. Arc<T> implements Send and Sync if and only if T implements both Send and Sync. For example, an object of type Arc<RefCell<U>> cannot be transferred across threads because RefCell does not implement Sync, consequently Arc<RefCell<U>> would not implement Send.

These two traits allow you to use the type system to make strong guarantees about the properties of your code under concurrency.

Threads

The thread::spawn() method accepts a closure, which is executed in a new thread. It returns a handle to the thread, that can be used to wait for the child thread to finish and extract its result:

use std::thread;
fn main() {
    let handle = thread::spawn(|| {
        "Hello from a thread!"
    });
    println!("{}", handle.join().unwrap());
}

move closures

We can force our closure to take ownership of its environment with the move

use std::thread;
fn main() {
    let x = 1;
    thread::spawn(move || {
        println!("x is {}", x);
    });
}

Safe Shared Mutable State

use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));
    for i in 0..3 {
        let data = data.clone();
        thread::spawn(move || {
            let mut data = data.lock().unwrap();
            data[0] += i;
        });
    }
    thread::sleep(Duration::from_millis(50));
}

Channels

use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;
fn main() {
    let data = Arc::new(Mutex::new(0));
    // `tx` is the "transmitter" or "sender".
    // `rx` is the "receiver".
    let (tx, rx) = mpsc::channel();
    for _ in 0..10 {
        let (data, tx) = (data.clone(), tx.clone());
        thread::spawn(move || {
            let mut data = data.lock().unwrap();
            *data += 1;
            tx.send(()).unwrap();
        });
    }
    for _ in 0..10 {
        rx.recv().unwrap();
    }





Friday, April 22, 2022

Go vs Rust - Strings

String

Go

In Go language, strings are different from other languages like Java, C++, Python, etc. it is a sequence of variable-width characters where each and every character is represented by one or more bytes using UTF-8 Encoding. Or in other words, strings are the immutable chain of arbitrary bytes(including bytes with zero value) or string is a read-only slice of bytes and the bytes of the strings can be represented in the Unicode text using UTF-8 encoding.

Due to UTF-8 encoding Golang string can contain a text which is the mixture of any language present in the world, without any confusion and limitation of the page.

    // Creating and initializing a slice of byte
    myslice1 := []byte{0x47, 0x65, 0x65, 0x6b, 0x73}
  
    // Creating a string from the slice
    mystring1 := string(myslice1)
    mystr := "Welcome to GeeksforGeeks ??????"
    // Finding the length of the string
    // Using len() function
    length1 := len(mystr)
  
    // Using RuneCountInString() function
    length2 := utf8.RuneCountInString(mystr)
    res1 := strings.Trim(str1, "@$")

Rust

There are two types of strings in Rust: String and &str.

A String is stored as a vector of bytes (Vec<u8>), but guaranteed to always be a valid UTF-8 sequence. String is heap allocated, growable and not null terminated.

&str is a slice (&[u8]) that always points to a valid UTF-8 sequence, and can be used to view into a String, just like &[T] is a view into Vec<T>.

let pangram: &'static str = "the quick brown fox jumps over the lazy dog";

let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // Note that s1 has been moved here and can no longer be used
print!("{} ", s3);

let mut s = String::from("lo");
s.push('l');
let s2 = "bar";
s.push_str(&s2);
print!("{} ", s);

String Slices as Parameters

A more experienced Rustacean would write the following linebecause it allows us to use the same function on both Strings and &strs:

fn first_word(s: &str) -> &str {

The concepts of ownership, borrowing, and slices are what ensure memory safety in Rust programs at compile time. The Rust language gives you control over your memory usage like other systems programming languages, but having the owner of data automatically clean up that data when the owner goes out of scope.

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // Error!
}

Bytes and Scalar Values and Grapheme Clusters

If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is ultimately stored as a Vec of u8 values that looks like this:

[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
224, 165, 135]

That’s 18 bytes and is how computers ultimately store this data. If we look at them as Unicode scalar values, which are what Rust’s char type is, those bytes look like this:

['न', 'म', 'स', '्', 'त', 'े']
There are six char values here, but the fourth and sixth are not letters: they’re diacritics that don’t make sense on their own. Finally, if we look at them as grapheme clusters, we’d get what a person would call the four letters that make up the Hindi word:

["न", "म", "स्", "ते"]

If we need to perform operations on individual Unicode scalar values, the best way to do so is to use the chars method.

for c in "नमस्ते".chars() {
    println!("{}", c);
}

Indexing into a string is often a bad idea because it’s not clear what the return type of the string indexing operation should be: a byte value, a character, a grapheme cluster, or a string slice. Therefore, Rust asks you to be more specific if you really need to use indices to create string slices.

let hello = "Здравствуйте";
let s = &hello[0..4];

s will be a &str that contains the first four bytes of the string. Earlier, we mentioned that each of these characters was two bytes, which means s will be Зд.
What would happen if we used &hello[0..1]? The answer: Rust will panic at runtime. So should use ranges to create string slices with caution, because it can crash your program.

Escape character

Go

Escape character Description
\\ Backslash(\)
\000 Unicode character with the given 3-digit 8-bit octal code point
\’ Single quote (‘). It is only allowed inside character literals
\” Double quote (“). It is only allowed inside interpreted string literals
\a ASCII bell (BEL)
\b ASCII backspace (BS)
\f ASCII formfeed (FF)
\n ASCII linefeed (LF
\r ASCII carriage return (CR)
\t ASCII tab (TAB)
\uhhhh Unicode character with the given 4-digit 16-bit hex code point.
Unicode character with the given 8-digit 32-bit hex code point.
\v ASCII vertical tab (VT)
\xhh Unicode character with the given 2-digit 8-bit hex code point.

Rust 

All number literals allow _ as a visual separator: 1_234.0E+18f64

\x41 7-bit character code (exactly 2 digits, up to 0x7F)
\n Newline
\r Carriage return
\t Tab
\\ Backslash
\0 Null

\u{7FFF} 24-bit Unicode character code (up to 6 digits)

\' Single quote
\" Double quote


Go vs Rust - HashMap

 HashMap

The concept of HashMap is present in almost all programming languages like Java, C++, Python, it has key-value pairs and through key, we can get values of the map. Keys are unique no duplicates allowed in the key but the value can be duplicated.

Go

Maps are Go’s built-in associative data type (sometimes called hashes or dicts in other languages).

package main
import "fmt"
func main() {
    m := make(map[string]int)
    m["k1"] = 7
    m["k2"] = 13
    fmt.Println("map:", m)
    v1 := m["k1"]
    fmt.Println("v1: ", v1)
    fmt.Println("len:", len(m))
    delete(m, "k2")
    fmt.Println("map:", m)
    _, prs := m["k2"]
    fmt.Println("prs:", prs)
    n := map[string]int{"foo": 1, "bar": 2}
    fmt.Println("map:", n)
}

To initialize a map with some data, use a map literal:

commits := map[string]int{
    "rsc": 3711,
    "r":   2138,
    "gri": 1908,
    "adg": 912,
}

Concurrency

Maps are not safe for concurrent use. One common way to protect maps is with sync.RWMutex.

var counter = struct{
    sync.RWMutex
    m map[string]int
}{m: make(map[string]int)}

To read from the counter, take the read lock:

counter.RLock()
n := counter.m["some_key"]
counter.RUnlock()
fmt.Println("some_key:", n)

To write to the counter, take the write lock:

counter.Lock()
counter.m["some_key"]++
counter.Unlock()

Rust

let mut gfg=HashMap::new();
// inserting records 
gfg.insert("Data Structures","90");
gfg.insert("Algorithms","99");
gfg.entry(String::from("Blue")).or_insert("50");

for (key, val) in gfg.iter() {
    println!("{} {}", key, val);
}

if gfg.contains_key( & "FAANG") {
   println!("yes it contains the given key well done gfg");
}

println!("len of gfg HashMap={}",gfg.len());

gfg.remove(& "key");
let value= gfg.get(&"Algorithms");

// Update after check
for word in text.split_whitespace() {
    let count = map.entry(word).or_insert(0);
    *count += 1;
}