Wednesday, January 13, 2021

Generic Constraints, Generic Methods, and Generic Factories in TypeScript

In Introduction to Generics in TypeScript the idea of generics was introduced. It was shown how generics provide a mechanism for having a balance between writing code that is flexible enough to apply to a broad range of types but not too flexible that type safety is lost. 

This post will be a short one that builds on that and shows a couple of extra things that can be achieved when using Generics in TypeScript. Three things will be shown in this post: Generic Constraints, Generic Methods, and Generic Factories

Monday, January 11, 2021

Introduction to Generics in TypeScript

Generic provides a mechanism for writing code that is flexible enough to apply to a broad range of types but not too flexible that type safety is lost. 

What exactly does the above statement mean?

To demonstrate what it means we will define a class and a function that makes use of Generics. The generic class will be an implementation of a List, which only allows adding and retrieving an item and the generic function will be the identity function

Both the List data structure and the Identity function are good examples because they are straightforward enough to allow for easy demonstration of the generic concepts.

Let’s start with List.

Introduction to Advance Types in TypeScript

This is the start of a series of posts where I take a look at aspects of TypeScript's type system that can be referred to as "Advanced".  See this as an exploration of TypeScript’s type system past Classes and Interfaces.

A good mental model to have when exploring the advanced part of TypeScript type system is to see it as a mechanism for more sophisticated ways to create Types. 

The normal, non-advanced ways of creating types in TypeScript involve using features of the language like type aliasclass and interface

For example these: 

interface IPerson {
  name: string
  age: number
}

type TPerson = {
  name: string
  age: number
}

class CPerson {
  constructor(private name:string, private age: number) {}
  
  getName() {
    return this.name;
  }

  getAge() {
    return this.name;
  }
}

With the advanced type features, types can be constructed directly or indirectly based on other existing types. How exactly this is done, will be the subject of this series of posts.

The posts in the series include:

Sunday, January 10, 2021

How to apply type annotations to functions in TypeScript

This post explores the various ways of ascribing functions with type annotations in TypeScript. To fully appreciate the various ways of ascribing type annotations to functions, we would first take a quick look at the various ways functions can be defined in JavaScript. Once that is done, we then look at how to bring TypeScript static type annotation into the picture. 

There are mainly two ways of defining functions in JavaScript. Namely: 
  • Function expressions 
  • Function declarations. 
Let’s quickly go over how these look

Monday, September 07, 2020

How to find out which package a file belongs to on Linux/Unix.

This post shows how to find out which software package created a file on a Linux system. The way to go about this task is dependent on the package manager is used. This post shows how it is done in 3 of the most popular package manager: RPM (Redhat/Centos, etc.), dpkg (Debian/Ubuntu, etc.), and pkg (FreeBSD)

rpm -qf <path/to/file>
pkg which <path/to/file>
dpkg-query -S <path/to/file>

Running the commands on CentOs and Ubuntu to find which package created /etc/ssh/ssh_config will look like this:

rpm -qf /etc/ssh/ssh_config
openssh-clients-5.3p1-124.el6_10.x86_64

dpkg-query -S /etc/ssh/ssh_config
openssh-client: /etc/ssh/ssh_config

Thursday, June 11, 2020

How to cross compile Rust on MacOs and target Linux using Docker container

I use a Mac for development, and recently I had to compile a Rust project to be run on a Linux server. This post captures how I managed to get this done. Note that the method highlighted in this post can also be used if you have a Windows (or even a Linux) dev environment.

The first thing I attempted was to install the necessary toolchain that should, in theory, allow me to cross-compile the Rust project on my Mac and target Linux. This did not go as smoothly as I would have loved. The first issue I encountered was with OpenSSL. After fixing that, the next issue was with backtrace-sys with the build failing with `error: failed to run custom build command for backtrace-sys v0.1.34`

This looks like I probably do not have the right environment/toolchain/dependencies installed.

I had the option of trying to identify all the required dependencies, but I was really running out of time and frankly, hunting around for the correct dependencies needed to set up a working compiler toolchain is also not fun. So I ended up making use of Docker to help with the build processes.

What I did was to create an image based on alpine Linux with the required toolchain. I then use the container spurn based on the image for the build processes. The procedure is as follows:

The Dockerfile:

FROM alpine:3.11 AS build
LABEL maintainer="Dadepo Aderemi"

#
# -- Install Rust build toolchain
#
ENV RUSTFLAGS="-C target-feature=+crt-static"
RUN apk add rust cargo openssl-dev

The +crt-static ensures statically linking of runtime dependencies. For more on this see Static and dynamic C runtimes

Now make sure to be in the same directory the docker file is located and create the image by running:

docker build -t rust-linux-builder .

Once built, run and connect into the container with the source code of the project mounted as volume by running:

docker run -it --rm --net=host -v $(pwd):/build rust-linux-builder

Note using $(pwd) assumes your current directory is where the source of the project is located. Also the use of the --rm flag ensures the container is removed automatically on exit. No need to keep the container around after finishing with the build.

Once in the container, switch to /build then run the build command:

/ # cd /build/
/build # cargo build --target x86_64-alpine-linux-musl --release

Once the build completes, exit the container and the binary would be found within the /target directory. Which can then be copied and installed on the linux server where it would be executed.

As you can see, there is nothing complicated in the Dockerfile and you can easily create your own, with your modification if need be. But in case you do not want to do that, I pushed the Docker image to Docker hub. This means you can easily get your rust project, targeting Linux by running:

docker run -it --rm --net=host -v $(pwd):/build dadepo/rust-linux-builder

...and follow the steps outlined above.

Sunday, March 01, 2020

Learning Rust - Day 10 - Smart Pointers

This is the 10th entry of my learning Rust journal...

It captures some of the learning points while going through chapter 15 of the Rust Book. You can read other posts in this series by following the label learning rust.

I enjoyed reading this chapter. I found it particularly interesting because it was about concepts I usually do not need to think about when working with the other programming languages I have used before now. Apart from that, it also allowed me to invalidate some wrong assumptions I had picked up along the way and also consolidates some of the concepts I have been learning.

One of the assumptions I had, which I found out was wrong while going through this chapter has to do with Stack vs Heap. For some strange reason, I had thought that Structs and Enums are always on the Heap. I suspect my familiarity with Java is to blame for this wrong assumption, since if you squint hard enough a struct looks like an Object in Java and usage of new keyword always means allocating memory on the heap.  But this is not the case in Rust. A struct or an enum does not automatically mean heap memory allocation.