├── static ├── css │ ├── input.css │ └── output.css ├── img │ ├── me.jpg │ ├── me1.jpg │ ├── me2.jpg │ ├── posts.webp │ ├── post │ │ ├── 0.webp │ │ ├── 1.webp │ │ ├── 10.webp │ │ ├── 2.webp │ │ ├── 3.webp │ │ ├── 4.webp │ │ ├── 5.webp │ │ ├── 6.webp │ │ ├── 7.webp │ │ ├── 8.webp │ │ └── 9.webp │ ├── logo-dark.svg │ └── logo.svg └── js │ └── index.js ├── posts.html ├── index.html └── post ├── formatting-strings-in-rust.html ├── rust-blanket-implementations.html ├── how-i-generate-this-site.html ├── async-rust.html ├── rust-generics.html ├── rust-lifetimes.html ├── trying-neovim.html └── random-numbers-in-rust.html /static/css/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base;@tailwind components;@tailwind utilities -------------------------------------------------------------------------------- /static/img/me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/me.jpg -------------------------------------------------------------------------------- /static/img/me1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/me1.jpg -------------------------------------------------------------------------------- /static/img/me2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/me2.jpg -------------------------------------------------------------------------------- /static/img/posts.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/posts.webp -------------------------------------------------------------------------------- /static/img/post/0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/0.webp -------------------------------------------------------------------------------- /static/img/post/1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/1.webp -------------------------------------------------------------------------------- /static/img/post/10.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/10.webp -------------------------------------------------------------------------------- /static/img/post/2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/2.webp -------------------------------------------------------------------------------- /static/img/post/3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/3.webp -------------------------------------------------------------------------------- /static/img/post/4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/4.webp -------------------------------------------------------------------------------- /static/img/post/5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/5.webp -------------------------------------------------------------------------------- /static/img/post/6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/6.webp -------------------------------------------------------------------------------- /static/img/post/7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/7.webp -------------------------------------------------------------------------------- /static/img/post/8.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/8.webp -------------------------------------------------------------------------------- /static/img/post/9.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/www.phillip-england.com/main/static/img/post/9.webp -------------------------------------------------------------------------------- /static/img/logo-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/js/index.js: -------------------------------------------------------------------------------- 1 | class TwMarkdown extends HTMLElement{constructor(){super()}connectedCallback(){const e=Array.from(this.children).map(e=>e.cloneNode(!0));this.innerHTML="",e.forEach(this.styleElement),e.forEach(e=>this.appendChild(e))}styleElement=e=>{const t=e.nodeName.toLowerCase();switch(t){case"pre":e.classList.add("custom-scroll","p-4","text-sm","overflow-x-auto","rounded","mb-4");break;case"h1":e.classList.add("font-bold","text-3xl","pb-4");break;case"h2":e.classList.add("font-bold","text-2xl","pb-4","pt-4","border-t","dark:border-gray-800");break;case"h3":e.classList.add("font-bold","text-xl","mt-6","mb-4");break;case"p":e.classList.add("text-sm","leading-6","mb-4");break;case"ul":e.classList.add("pl-6","mb-4","list-disc");break;case"ol":e.classList.add("pl-6","mb-4","list-decimal");break;case"li":e.classList.add("mb-2","text-sm");break;case"blockquote":e.classList.add("ml-4","pl-4","border-l-4","border-gray-300","italic","text-gray-700");break;case"code":e.parentElement.nodeName.toLowerCase()!=="pre"&&e.classList.add("font-mono","px-1","rounded","text-sm","border","border-gray-200","dark:border-gray-800");break;case"hr":e.classList.add("border-t","border-gray-300","dark:border-gray-800","my-4");break;case"a":e.classList.add("text-blue-800","dark:text-blue-500","underline");break;case"img":e.classList.add("max-w-full","h-auto","rounded","my-4");break}Array.from(e.children).forEach(this.styleElement)}}class RandomBeads extends HTMLElement{connectedCallback(){this.classList.add("flex","flex-row","gap-2");const n=this.getAttribute("count"),t=parseInt(n);if(isNaN(t)){console.error(' requires an integer in the "count" attribute');return}this.beads=[];let e=4;for(let o=0;othis.transitionBeadColors(),2e3)}generateRandomColor(){return{r:Math.floor(Math.random()*256),g:Math.floor(Math.random()*256),b:Math.floor(Math.random()*256)}}transitionBeadColors(){this.beads.forEach(e=>{const t=this.generateRandomColor();e.style.backgroundColor=`rgb(${t.r}, ${t.g}, ${t.b})`})}disconnectedCallback(){this.colorIntervalId&&clearInterval(this.colorIntervalId)}}class TheBlinker extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){const n=parseInt(this.getAttribute("rate")||"1000"),e=document.createElement("span");e.textContent=this.textContent||"_";const t=document.createElement("style");t.textContent=` 2 | @keyframes blink { 3 | 0%, 100% { opacity: 1; } 4 | 50% { opacity: 0; } 5 | } 6 | span { 7 | animation: blink ${n}ms step-end infinite; 8 | } 9 | `,this.shadowRoot.appendChild(t),this.shadowRoot.appendChild(e)}}class TitleLinks extends HTMLElement{constructor(){super()}connectedCallback(){const e=this.getAttribute("target"),n=this.getAttribute("link-class"),s=this.getAttribute("link-wrapper-class"),o=n.split(" "),i=s.split(" "),a=parseInt(this.getAttribute("offset"),10)||0,t=document.querySelector(e);if(!t){console.error(`Target element "${e}" not found.`);return}const r=t.querySelectorAll("h1, h2, h3, h4, h5, h6");r.forEach(e=>{if(e.id){const n=document.createElement("div");i.forEach(e=>{n.classList.add(e)});const t=document.createElement("a");o.forEach(e=>{t.classList.add(e)}),t.classList.add("title-link"),t.href=`#${e.id}`,t.textContent=e.textContent,n.appendChild(t),this.appendChild(n)}});const c=document.createElement("style");this.appendChild(c),this.addEventListener("click",e=>{if(e.target.tagName==="A"){e.preventDefault();const t=e.target.getAttribute("href").substring(1);history.pushState({},document.title,window.location.pathname+"#"+t);const n=document.getElementById(t);if(n){const e=n.getBoundingClientRect().top+window.pageYOffset+a;window.scrollTo({top:e,behavior:"smooth"})}}})}}class CustomScroll extends HTMLElement{constructor(){super()}connectedCallback(){this.innerHTML=` 10 | 32 | `}}class HashTitleScroll extends HTMLElement{connectedCallback(){let s=parseInt(this.getAttribute("offset"),10)||0,o=window.location.href,e=o.split("/"),t=e[e.length-1];if(!t.includes("#"))return;let i=t.split("#")[1],n=document.getElementById(i);if(!n)return;const a=n.getBoundingClientRect().top+window.pageYOffset+s;window.scrollTo({top:a,behavior:"smooth"})}}window.addEventListener("DOMContentLoaded",()=>{customElements.define("the-blinker",TheBlinker),customElements.define("tw-markdown",TwMarkdown),customElements.define("random-beads",RandomBeads),customElements.define("title-links",TitleLinks),customElements.define("hash-title-scroll",HashTitleScroll),customElements.define("custom-scroll",CustomScroll)}) -------------------------------------------------------------------------------- /posts.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /static/css/output.css: -------------------------------------------------------------------------------- 1 | *,::before,::after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::before,::after{--tw-content:''}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,liberation mono,courier new,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::before,::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-\[85px\]{top:85px}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.flex{display:flex}.table{display:table}.hidden{display:none}.h-6{height:1.5rem}.h-8{height:2rem}.h-\[50px\]{height:50px}.h-\[85px\]{height:85px}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.w-6{width:1.5rem}.w-8{width:2rem}.w-\[150px\]{width:150px}.w-\[300px\]{width:300px}.w-full{width:100%}.max-w-full{max-width:100%}.flex-grow{flex-grow:1}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-scroll{overflow-y:scroll}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-t{border-top-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pt-4{padding-top:1rem}.pt-\[135px\]{padding-top:135px}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,liberation mono,courier new,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.uppercase{text-transform:uppercase}.italic{font-style:italic}.leading-6{line-height:1.5rem}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.text-dracula-red{--tw-text-opacity:1;color:rgb(255 85 85/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.opacity-50{opacity:.5}.filter{filter:var(--tw-blur)var(--tw-brightness)var(--tw-contrast)var(--tw-grayscale)var(--tw-hue-rotate)var(--tw-invert)var(--tw-saturate)var(--tw-sepia)var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:150ms}.duration-1000{transition-duration:1e3ms}.hover\:underline:hover{text-decoration-line:underline}@media(min-width:640px){.sm\:left-\[5\%\]{left:5%}.sm\:w-\[90\%\]{width:90%}.sm\:pl-\[5\%\]{padding-left:5%}.sm\:pr-\[5\%\]{padding-right:5%}}@media(min-width:768px){.md\:left-\[10\%\]{left:10%}.md\:w-\[80\%\]{width:80%}.md\:pl-\[10\%\]{padding-left:10%}.md\:pr-\[10\%\]{padding-right:10%}}@media(min-width:1024px){.lg\:left-\[20\%\]{left:20%}.lg\:w-\[60\%\]{width:60%}.lg\:pl-\[20\%\]{padding-left:20%}.lg\:pr-\[20\%\]{padding-right:20%}}@media(min-width:1280px){.xl\:left-0{left:0}.xl\:top-0{top:0}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-\[300px\]{width:300px}.xl\:w-\[40\%\]{width:40%}.xl\:pl-\[30\%\]{padding-left:30%}.xl\:pr-\[30\%\]{padding-right:30%}}.dark\:flex:where(.dark,.dark *){display:flex}.dark\:hidden:where(.dark,.dark *){display:none}.dark\:border-gray-800:where(.dark,.dark *){--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity))}.dark\:bg-black:where(.dark,.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.dark\:bg-dracula-background:where(.dark,.dark *){--tw-bg-opacity:1;background-color:rgb(40 42 54/var(--tw-bg-opacity))}.dark\:text-blue-500:where(.dark,.dark *){--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.dark\:text-gray-200:where(.dark,.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.dark\:text-gray-400:where(.dark,.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.dark\:text-white:where(.dark,.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))} -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[phillip england]

🎂 born on 12/9/2024

learning in public

30 |

Recent Posts

  1. Trying Neovim 12/13/2024
  2. Async Rust 12/14/2024
  3. Formatting Strings in Rust 12/15/2024
  4. Random Numbers in Rust 12/15/2024
  5. How I Generate This Site 12/15/2024
  6. Structs, Traits, and Enums in Rust 12/15/2024
  7. Rust Hyper 12/16/2024
  8. Iterators in Rust 12/17/2024
  9. Rust Lifetimes 12/18/2024
  10. Rust Generics 12/19/2024
  11. Rust Blanket Implementations 12/20/2024

Welcome,

My name is Phillip England, and I am passionate about programming. This website is my notebook. Here, I will be documenting my software development educational journey.

I began coding in 2019 when I became interested in making a website. I still remember when I ran my first for loop in the browser console. Since then, I've been programming as a hobby. Around 3 years ago, I ramped up my learning and have been waking up at 4AM to study.

A while back, I thought to myself, "I would probably keep coding even if it was never a career for me." That's when I knew I needed to start documenting my learning. There is a chance this is a craft I keep with me for a lifetime regardless of my professional endeavours.

That in and of itself might be worth something.

Learning Wishlist

When I think to myself, "I need to learn that!", I will document the thought here.

  • Learn about blanket implementations in Rust. They basically allow you to create methods which apply to a multitude of types. I discovered this idea when starting my string parsing lib, stur.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/formatting-strings-in-rust.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[formatting strings in rust]

written 12/15/2024

format!("{}", your_mom)

30 |

Formatting Strings

I asked Claude how to format strings in Rust. It introduced me to the format! macro.

Here is what Claude had to say.

In Rust, there are several ways to format strings. I'll show you the most common methods:

  1. Using format! Macro
1let name = "Alice";
31 | 2let age = 30;
32 | 3let formatted = format!("My name is {} and I am {} years old", name, age);
33 | 4println!("{}", formatted);
34 | 
  1. Using Positional Arguments
1let formatted = format!("First: {0}, Second: {1}, First again: {0}", "hello", "world");
35 | 
  1. Named Parameters
1let formatted = format!("Name: {name}, Age: {age}", name="Bob", age=25);
36 | 
  1. Specifying Formatting Traits
1// Debugging format
37 | 2let debug_str = format!("Debug: {:?}", vec![1, 2, 3]);
38 | 3
39 | 4// Precision for floating-point numbers
40 | 5let pi = 3.14159;
41 | 6let precise = format!("Pi to two decimal places: {:.2}", pi);
42 | 
  1. Padding and Alignment
1let padded = format!("{:10}", "hi");     // Right-aligned, width 10
43 | 2let left_aligned = format!("{:<10}", "hi"); // Left-aligned, width 10
44 | 3let zero_padded = format!("{:0>5}", 42);   // Zero-padded to width 5
45 | 

The format! macro is versatile and allows you to create formatted strings without printing them, while println! directly prints to the console. The syntax is consistent across these methods, making string formatting in Rust quite flexible and powerful.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/rust-blanket-implementations.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[rust blanket implementations]

written 12/20/2024

extending core types in Rust

30 |

Sandbox

The sandbox for this article can be found here.

My conversation with ChatGPT regarding the topics can be found here.

Discovery

I wanted to get started working on a simple string parsing library in Rust to get my feet wet, and to build a low-level primitive tool I can bring with me into future projects.

I basically want something that will allow me to do quick string operations I find myself performing frequently.

Here is a tiny Go implementation of the sort of thing I am discussing.

Just some quick and dirty functions.

Rust is Different

Upon researching how to lay out the library and the best methods to ensure my users can work with both String and &str, I came across the idea of blanket implementations.

In Go, it made sense to use functions because Go only has one string type. But since Rust has multiple string types, we need to think about how to incorporate multiple types in our library.

I thought to myself, "Can I just create a trait and apply it to both String and &str?"

The answer is, yes, using blanket implementations.

Compare to Javascript

I think looking at how Javascript and Rust handle this idea is important as the Javascript example is a bit more easy to digest, but it has the same underlying idea.

In Javascript, let's imagine we want to take some object and extend it. Well, we could do something like this in the browser:

1window.hello = () => {
31 | 2    console.log('hello from the window');
32 | 3}
33 | 

Then, later on in our program, we may call the method like:

1window.hello();
34 | 2// hello from the window
35 | 

Javascript is prototype-based and allows you to directly mutate core objects in the language.

Rust does not provide us the ability to do this. So, in Rust, instead of directly altering core types, we can extend them using traits.

Traits as Extensions

Traits can be implemented on types in a unique variety of ways.

NOTE TO SELF: Here I am again, discussing traits in Rust. Of all the features I've come across, traits seem to be one of the most fundamental building blocks of Rust.

Let's first start by defining a trait we wish to implement on &str and String:

1pub trait SomeExtension {
36 | 2    fn shout(&self) -> String;
37 | 3}
38 | 

The AsRef Trait

Rust has a trait named, AsRef which can be used to implement other traits on multiple core types at once.

Before we dive into how to use AsRef to implement SomeExtension on both &str and String, we should take a closer look at how AsRef actually works.

You can find the docs for AsRef here along with a page that works through the AsRef<T> trait versus the Borrow trait.

One of the first things I've noticed is how the Rust standard library has a whole module called convert which might be useful to take a deeper dive into later. It might be a good idea to just study the standard library and see how the core Rust team codes their own data types.

NOTE TO SELF: Did you take the time to study the Rust standard library?

Implementing Our Extension

Now that we know AsRef can be used to implement traits on multiple types in a generic way, we can use it to implement SomeExtension on both &str and String types.

To implement SomeExtension, write:

1impl<T> SomeExtension for T
39 | 2where
40 | 3    T: AsRef<str>,
41 | 4{
42 | 5    fn shout(&self) -> String {
43 | 6        let s = self.as_ref();
44 | 7        format!("{}!", s.to_uppercase())
45 | 8    }
46 | 9}
47 | 

Then, we can use our extension like so:

1fn main() {
48 | 2    let some_str = "i am going to shout";
49 | 3    let shout = some_str.shout();
50 | 4    let some_string = String::from("i also shout");
51 | 5    let shout2 = some_string.shout();
52 | 6    println!("{} - {}", shout, shout2);
53 | 7}
54 | 

As you can see, &str and String can now use the shout method from the MyExtension trait.

Conclusion

Again, I am finding myself in a place where I feel the need to really dive deep into traits in Rust. They keep coming up and their generic syntax is one of the most challenging things about reading the language and getting a good mental model of it.

I am thinking I skim through The Book over the next few days and just get some more general exposure to Rust concepts and to get better at looking at the code.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/how-i-generate-this-site.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[how i generate this site]

written 12/14/2024

check out my tool, flint

30 |

Easy Peasy

It really is. Using flint, I run flint spark. The following is the resulting output, which walks you through each step flint takes as it bundles up your static assets.

 1
31 |  2  ##########  ##   ##    ###      ##  ########
32 |  3  ##          ##         ####     ##     ##
33 |  4  #######     ##   ##    ## ##    ##     ##
34 |  5  ##          ##   ##    ##  ##   ##     ##
35 |  6  ##          ##   ##    ##   ##  ##     ##
36 |  7  ##          ##         ##    ## ##     ##
37 |  8  ##          #########  ##     ####     ##
38 |  9-------------------------------------------------
39 | 10Language-Agnostic Static Sites
40 | 11-------------------------------------------------
41 | 12🔥 sparking flint
42 | 13🗃️ searching for flint.json
43 | 14🔎 parsing routes from flint.json
44 | 15🏹 making an http request to each route
45 | 16🔨 generating static html assests
46 | 17🗑️ removing ./out
47 | 18✍️ writing minified, static html to ./out
48 | 19🖌️ copying over minified, static assests from ./static
49 | 20✏️ copying over the favicon from ./favicon.ico to ./out
50 | 21⚠️ Favicon does not exist at: ./favicon.ico
51 | 22📚 your assets have been bundled at ./out
52 | 23🙏 thank you for using flint
53 | 24⭐ dont forget to give me star at: https://github.com/phillip-england/flint
54 | 

Why Flint?

You see, I found myself repeating a common pattern in my web applications. I consider myself fairly effecient with Go, and I can get a web server up and running with some html pages quickly.

I don't want to be locked into a certain way of doing things.

That's why I built flint, a minimal static site generator for people who can already get their static site running on a localhost server.

Why use some big static site generator when you might not have to?

Follow Along

I can explain how flint works, but it's probably easier if you just follow along. Below, I will walk you through how to install this website, install flint, and use flint to generate a minified, static version of this website.

Install This Website

Clone the repo:

1git clone https://github.com/phillip-england/portfolio-site
55 | 

You'll need go version 1.23.3 or later.

Once installed, cd into the project and serve it:

1cd portfolio-site
56 | 2go run main.go
57 | 

Okay, now we can leave this application running in the background.

Installing Flint

In a new terminal, clone the repo:

1git clone https://github.com/phillip-england/flint
58 | 

Then install:

1cd flint
59 | 2go install
60 | 

You'll need to verify go properly installed the binary on your PATH. After, reload your terminal to ensure the changes take effect.

To verify installation, run flint help.

flint.json

Inside of portfolio-site you should see a flint.json. Go ahead and take a look:

 1{
61 |  2    "host": "http://localhost:8080",
62 |  3    "static": "./static",
63 |  4    "favicon": "./favicon.ico",
64 |  5    "out": "./out",
65 |  6    "target": "https://phillip-england.com",
66 |  7    "routes": [
67 |  8        "/",
68 |  9        "/posts",
69 | 10       
70 | 11        ... more routes
71 | 12    ]
72 | 13}
73 | 

This config tells flint where to ping, which routes to ping, and where all the static assets are located. It also tells flint where the website will be deployed so all href values can be changed to absolute paths pointing towards the deployment location.

Generating The Site

Navigate back to the portfolio-site and make sure it is still running in the background on localhost:8080.

Once you've ensured the website is still running locally, run the following command from within portfolio-site:

1flint spark
74 | 

Afterward, you should see a new directory named ./out at /portfolio-site/out.

This directory contains all the static assets for the site and can easily be dropped into any static deployment environment.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/async-rust.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[async rust]

written 12/14/2024

can anyone understand this language?

30 |

Article Sandbox

I will be running experiments in my own editor and will be saving the repo here.

My Experience With Rust

Rust is going to be my language for 2025. I've tinkered with a low-level networking framework called Zeke and I would like to continue working on that project.

Since working on Zeke, I've learned a bit more about programming on an interface level, and I think those skills will help a lot.

Rust has a few concepts that are challenging to me such as lifetimes and the borrow checker. But the number one thing which caused me challenges when writing zeke was multi-threaded rust.

If I had a good grapse on those concepts I would feel way more comfortable in the language. I think before I start diving into all the different multi-threaded types rust provides, I need to understand it's async model better.

Let's Get Rusty

Any time I think about rust, I think about Bodgen from Let's Get Rusty on youtube.

This video is where I am going to start.

Futures

The first big takeaway is that async functions in rust are just a facade over a function which returns a Future.

1async fn my_function() {
31 | 2    println!("I'm an async function!")
32 | 3}
33 | 

is really just a fasade over:

1fn my_function() -> impl Future<Output = ()> {
34 | 2    println!("I'm an async function too!")
35 | 3}
36 | 

I visited the docs and it looks like Future is a trait which can be polled to completetion.

When we use the await keyword, we are using a fasade over the poll method which is associated with the Future trait.

Tokio

Bodgen explains that Futures must be manually polled to completetion, which is cumbersom. But, that is why a runtime like tokio exists.

In a language like Javascript, Promises are handled by the language underneath the hood. But in Rust, the async runtime is not included in the std lib, so options like tokio have emerged.

I went ahead and added tokio to my cargo.toml:

1[package]
37 | 2name = "sandbox-async-rust"
38 | 3version = "0.1.0"
39 | 4edition = "2021"
40 | 5
41 | 6[dependencies]
42 | 7tokio = { version = "1", features = ["full"] }
43 | 

Tokio Tasks

Tasks are used to make our code run concurrently. Tasks a green threads and are non-blocking similar to gorountines.

I discovered that tokio attempts to mimic the api provided by the Rust std lib for traditional threads. This makes it easy to swap between using tasks and traditional threads without a paradigm shift.

Futures Are Lazy

This is something to be noted. Futures are lazy in Rust which means we can collect our tasks and then call await on them later. If we do not await a task, then we do not experience any runtime cost for the task.

This is different than other languages that use the async/await syntax to handle asyncronous code.

Morphing Data Across Threads

After a bit of playing around, I found myself wondering how to make changes to data across multiple threads.

I ended up with something like this:

 1async fn morph_data_across_threads() {
44 |  2    let str = String::from("I will be morphed!");
45 |  3    let str_arc = Arc::new(Mutex::new(str));
46 |  4    let mut handles = vec![];
47 |  5    for i in 0..10 {
48 |  6        let str_clone = Arc::clone(&str_arc);
49 |  7        let task = tokio::spawn(async move {
50 |  8            // lock the mutex to modify it
51 |  9            let mut val = str_clone.lock().await;
52 | 10            val.push_str(&i.to_string());
53 | 11        });
54 | 12        handles.push(task);
55 | 13    }
56 | 14    for task in handles {
57 | 15        task.await.unwrap();
58 | 16    }
59 | 17    // retrieve the final value as str is expended
60 | 18    let final_str = str_arc.lock().await;
61 | 19    println!("{}", final_str);
62 | 20}
63 | 

And with this we see the introduction of a few core types I think I'll need to study. I see Arc and Mutex. We are calling lock() and these are concepts I think I'll need to get a better grasp on.

The code runs, and I have a general understanding as to what is going on under the hood, but from what I understand about Rust, the way these types of constructs impact memory is important to get right.

Smart Pointers

After I started researching, I came across this video on smart pointers.

ChatGPT says: "A smart pointer is an object that acts like a pointer but provides additional features to manage the ownership, lifecycle, and memory of dynamically allocated resources. It is typically used in programming languages like C++ and Rust to handle memory safely and efficiently."

So, it looks like these constructs are called smart pointers. I am going to dig through them and do my best to get a surface level understanding of them.

Box

Box<T> enables us to dictate that some data should be stored on the heap instead of the stack.

We store things on the heap when we have no way of knowing the size of the data at compile time. You want to try and avoid storing things on the heap, but in certain situations it cannot be avoided.

The video points out Box<T> has 2 use-cases:

  1. When we have a variable with a trait type which cannot be computer at compile time.
  2. When we have a recursive data type whos fields include the struct it is derived from.

NOTE TO SELF: Go back and finish "async rust" when you have a better understanding of traits and lifetimes.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/rust-generics.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[rust generics]

written 12/19/2024

frosted mini squares

30 |

Sandbox

The sandbox where I tested out these ideas is here.

The conversation I had with ChatGPT regarding the topic is here.

Generics Are Used Everywhere

Yesterday, I was learning about lifetimes and I kept coming across generic notations. I understood most of them but wanted to do a deep dive to make sure I do not miss anything important or obscure.

One thing is for sure, generics are used extensivly in Rust code. I see the notations all over the place and they have a tendancy to make the code feel "full" and a bit harder to navigate.

I think having a solid understanding of generics will help my brain feel more at ease when scanning Rust code.

Avoiding Code Duplication

Generics are all about avoiding code duplication. Since Rust is a statically typed language, you end up in scenarios where you might have a similar function for multiple types.

Take these two functions:

1fn add_strs(s1: String, s2: String) -> String {
31 | 2    s1 + &s2
32 | 3}
33 | 4
34 | 5fn add_nums(x: i32, y: i32) -> i32 {
35 | 6    x + y
36 | 7}
37 | 

They can be converted into this generic function:

1fn add_generic<T: std::ops::Add<Output = T>>(x: T, y: T) -> T {
38 | 2    x + y
39 | 3}
40 | 

I learned a few things while drafting out these above examples. Let's take note of this tidbit right here <T: std::ops::Add<Output = T>>.

Rust Constrains

OK, this is an idea I like and think is a core theme found throughout Rust. Rust is a constraining language. It is trying to place tons of caps and limits on what you can do and how you can manage the flow of data throughout your program.

When we say <T: std::ops::Add<Output = T>>, we are limiting the passed in types to types which implement the Add trait from the standard library.

ChatGPT made a good point on this. If we do not limit the passed in types, we may end up in a situation where the function does not return a value of type T.

In short, these type constraints help us to be confident that the function is going to return the type of data that it says it will return.

Learning more about traits and trait bounds is a sure way to get a better grip on generics as they are related closely together in this way.

Constraints Are Contextual

Rust may require a function or method be constrained depending on how the inputs are used. For example, in the above function add_generic, we see that we are operating on the inputs using the addition symbol +. Because of this context, Rust now requires a constraint.

Had we never added the values together, the constraint would no longer be needed.

Rust provides a multitude of traits to constrain your generic types with depending on how they are used within your code.

This is all possible because the Rust compiler is so thorough. When we complain about Rust having a slow compiler, these are the types of checks which have the potential to slow things down.

All in the name of safety, baby.

Implementing Add on a Custom Type

So, what if we want to use a custom type with our add_generic function? Well, we can implement the Add trait and tell Rust how they type ought to be used when used in conjunction with addition operator.

First, our custom type:

1#[derive(Debug)]
41 | 2struct Point {
42 | 3    x: i32,
43 | 4    y: i32,
44 | 5}
45 | 

Then, we implement the Add trait:

 1impl std::ops::Add for Point {
46 |  2    type Output = Point;
47 |  3
48 |  4    fn add(self, other: Point) -> Point {
49 |  5        Point {
50 |  6            x: self.x + other.x,
51 |  7            y: self.y + other.y,
52 |  8        }
53 |  9    }
54 | 10}
55 | 

Finally, we can add our points:

1let p1 = Point {x: 10, y: 10};
56 | 2let p2 = Point {x: 10, y: 10};
57 | 3let p3 = add_generic(p1, p2);
58 | 4dbg!(p3);
59 | 

Other Generic Contexts

As I was looking into this, I found it useful to learn about other contexts where our generic types might need to be constrained. After asking ChatGPT, here is what I came up with:

Arithmetic and Mathematical Traits

  • std::ops::Add: For + operator.
  • std::ops::Sub: For - operator.
  • std::ops::Mul: For * operator.
  • std::ops::Div: For / operator.
  • std::ops::Rem: For % operator.
  • std::ops::Neg: For unary - operator.
  • std::ops::Shl / std::ops::Shr: For bitwise left (<<) and right (>>) shifts.

Comparison Traits

  • std::cmp::PartialEq: For == and !=.
  • std::cmp::Eq: For strict equality (used in HashMap keys, requires PartialEq).
  • std::cmp::PartialOrd: For <, <=, >, and >=.
  • std::cmp::Ord: For total ordering (requires PartialOrd and Eq).

Iteration and Collection Traits

  • std::iter::Iterator: For types that can produce a sequence of values.
  • std::iter::IntoIterator: For types that can be converted into an iterator.
  • std::iter::Extend: For extending a collection with an iterator.
  • std::iter::FromIterator: For constructing a collection from an iterator.

Borrowing and Ownership Traits

  • std::borrow::Borrow: For generic borrowing.
  • std::borrow::ToOwned: For creating an owned version of a borrowed value (e.g., String from &str).
  • std::convert::AsRef: For converting a value to a reference of another type.
  • std::convert::AsMut: For converting a value to a mutable reference of another type.

Default and Debug Traits

  • std::default::Default: For providing a default value.
  • std::fmt::Debug: For formatting with {:?}.
  • std::fmt::Display: For formatting with {}.

Trait Object-Specific Traits

  • std::any::Any: For working with types at runtime.
  • std::marker::Send: For types that are safe to transfer between threads.
  • std::marker::Sync: For types that are safe to reference from multiple threads.

Functional Programming Traits

  • Fn: For closures that do not mutate state.
  • FnMut: For closures that mutate state.
  • FnOnce: For closures that consume their environment.

I/O Traits

  • std::io::Read: For types that can read data.
  • std::io::Write: For types that can write data.
  • std::io::Seek: For types that can seek within a stream.
  • std::io::BufRead: For buffered readers.

Common Combinations

  • Clone + PartialEq: For types that can be cloned and compared.
  • Iterator + Debug: For iterators that can also be debugged.
  • Send + Sync: For thread-safe types.

Conclusion

Generics in Rust are extremely important and a lack of understanding of them can result in code feeling full and archaic. Generics are tied directly to traits and are used in conjunction with them to ensure that return values are of the intended type.

All in all, when we are writing generic functions, we need to be mindful and aware that the context and way in which we use the input parameters will dictate the way in which we need to constrain them.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/rust-lifetimes.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[rust lifetimes]

written 12/18/2024

when are we..?

30 |

Sandbox

The repo where I tested out these ideas is here.

The conversation I had with ChatGPT about the topic is here. I do a lot of my personal study with ChatGPT, so the conversation is worth a peek as the majority of this content is derived from my studies there.

Afterthoughts

As I was writing this article, I noticed generics are really important regarding lifetimes. I have a pretty solid understanding of generics already, but I think going back and doing a post about them would be good after this.

NOTE TO SELF: Did you write a post about generics in Rust?

It's About Time

I've been avoiding this topic for a bit because of all the concepts in Rust, lifetimes are the one I am most likely to just rely on compile-time errors to help me manage and correct.

In short, when I write lifetimes, I have no idea what I am doing.

The only thing I can remember is a quote from Tristram Oaten on his YouTube channel, No Boilerplate.

He said, "..lifetimes let us know when our data is."

So, let's checkout The Book and see what it has to say on lifetimes.

The Book on Lifetimes

Right off the bat, we read a lifetime is, "..a construct the compiler (or more specifically, its borrow checker) uses to ensure all borrows are valid."

Despite not feeling confident about lifetimes, I am pretty familiar with the borrow-checker. I used Rust pretty early on in my programming journey (which is why I've revisted the language 3 times now). Because of this, I've actually inherited some good coding practices regarding how I think about handling data within a program.

The book also points out that it can be easy to confuse lifetimes and scopes. This is because scopes and lifetimes are closely related.

The real thing I notice here is that it is just important to know when your data is valid in your Rust programs.

I think a pattern I came across when I first started Rust is the idea that you can pass a variable into a function, us it within, and then return it if you need it back.

The Book has a page on how to explicitly annotate lifetimes using some_var<'a>

Let me see what ChatGPT has to say about the topic.

ChatGPT on Lifetimes

ChatGPT makes it clear lifetimes are used to ensure references are valid for the shortest amount of time possible. They enable us to tell the compiler when a reference is no longer accessible.

Here is an example where we try to make use of a &x when it is no longer available:

1fn dangling_reference() {
31 | 2    let r;
32 | 3    {
33 | 4        let x = 5;
34 | 5        r = &x;
35 | 6    }
36 | 7    // println!("{}", r); 💥 Dangling reference!
37 | 8}
38 | 

Why Does This Matter?

Lifetimes become a practical problem in your code when you start passing references around.

Take the following function for example:

1fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
39 | 2    if s1.len() > s2.len() {
40 | 3        s1
41 | 4    } else {
42 | 5        s2
43 | 6    }
44 | 7}
45 | 

In this function, we use -> &'a str to state: "The returned reference will be valid for the duration of lifetime 'a"

What is the duration of lifetime 'a you might ask?

The Shortest Lifetime

If a more than one reference is passed into a function, and the function returns a new reference, the new reference will mirror the shortest lifetime of the input references.

So, when we say state, (s1: &'a str, s2: &'a str) -> &'a str, what we are really saying is:

"The lifetime of the return value will match s1 if s1 has the shortest lifetime, or it will match s2 if s2 has the shortest lifetime."

This means when we are in a scenario where a reference is being passed from one location to another, we need to be mindful of all the data points it comes into contact with.

If we pipe a reference into a function, and that function outputs another reference, our data is now "linked" from the compiler's perspective.

Every Reference Has a Lifetime

One thing to note is that all the references in a Rust program have a lifetime, even if it is not explicitly annotated. Sometimes, Rust can even infer the lifetime of a return value. These are called the Elision Rules.

In short, elision rules allow us to forgo explicitly writing out our lifetime annotations.

A good rule of thumb is if a function only has one reference passed into it, then the annotation can be excluded because the return value will always match that of the input reference.

I would take a peak at the Elision Rules though because I am not diving into all the minute details here.

Common Scenarios

Here are a few common scenarios where lifetimes are found:

Structs

You may end up with a struct which has lifetime annotations like so:

1struct Car<'a> {
46 | 2    model: &'a str
47 | 3}
48 | 

Functions

As already discussed, functions may have lifetime annotations:

1fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
49 | 2    if x.len() > y.len() {
50 | 3        x
51 | 4    } else {
52 | 5        y
53 | 6    }
54 | 7}
55 | 

In Trait Bounds

We can also use lifetimes when using trait bounds:

1fn print_with_lifetime<'a, T>(item: &'a T)
56 | 2where
57 | 3    T: std::fmt::Display + 'a,
58 | 4{
59 | 5    println!("{}", item);
60 | 6}
61 | 

Of all the things I've listed so far, ^ that one looks the most archaic to me. I understand traits and their purpose, however, I need to brush up on actually using traits in real world scenarios.

NOTE TO SELF: Did you do a deep dive on trait bounds in Rust?

Lifetimes as Timelines

ChatGPT suggested thinking of lifetimes in terms of timelines. When approach a Rust program, we need to take special care to ensure we are being proactive about how we are thinking about our references.

Remember, each reference has it's own timeline, and if you borrow a reference, you need to make sure it lives long enough to be borrowed in the first place.

Conclusion

I think this first look at lifetimes is good, but I will definitely be back on this topic. All in all, lifetimes are really all about making sure that references are available for only the time they are needed and no more.

Lifetimes are Rusts way of avoiding common a pitfall like dangling references.

I have heard some complain about lifetimes "coloring" functions. Kind of similar to how when you mark a function async in typescript, your whole codebase ends up being marked with async.

I will be back on this one.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/trying-neovim.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[trying neovim]

written 12/13/2024

where are the casual vim users?

30 |

Points of Learning

The following article touches on these technical skills:

  • installing binaries and moving them around on your system
  • learn the basics of vim motions
  • gain exposure to find and replace methods in nvim
  • install a precustomized nvim version, NvChad

Preparing the Thumbnail

As I was drafting out this post, I was preparing a thumbnail for it (ai). During which I noticed the image for my home page on this website was 1mb in size. Way to large for a simple blog thumbnail. So, I resized it using Image Resizer and I got to thinking, "I bet a command line utility for this exists."

I got this image size down from 1mb to 7kb. The images in the thumbnails on this site are 150x150px and I have no intention of scaling them over time.

NOTE TO SELF: Find a command line utility to resize images on the fly.

My Experience With Nvim

I know the basics. I am not an expert on the key maps or anything but I have a general idea of how it works. I know you can do all your config in lua. I don't really know lua, but I know it is loosely typed so we are good.

I gave nvim a good go around a year or so ago, but I think it might be time for another stab. The main reason is I just want to run something more lightweight on my desktop. Sometimes she can't keep up 😅.

That, and honestly, the amount of die-hard vim users is curious to me. Am I missing something? Plus, I get a chance to look a little bit closer at a well-known software. Here we go.

Installation

I found installing a bit of a pain as version as version 0.7.2 was installed by default on WSL when running:

1sudo apt-get install neovim
31 | 

Let's uninstall, update, and reinstall:

1sudo apt-get remove neovim
32 | 2sudo apt-get update
33 | 3sudo apt-get upgrade
34 | 4sudo apt-get install neovim
35 | 

Still getting 0.7.2..

Found this post on reddit and tried this:

1sudo apt-get install software-properties-common
36 | 2sudo add-apt-repository ppa:neovim-ppa/stable
37 | 3sudo apt-get update
38 | 4sudo apt-get install neovim
39 | 5nvim -v
40 | 

Still getting 0.7.2..

Building From Source

I can build from source on the nvim release page.

I ended up doing:

1wget https://github.com/neovim/neovim/releases/download/v0.10.2/nvim-linux64.tar.gz
41 | 2tar -xvzf nvim-linux64.tar.gz
42 | 3mv nvim-linux64 nvim
43 | 

I am not 100% sure where this needs to go on my system. I know neovim is config-heavy so I want to be sure I set this up right.

...

Okay I see /usr/bin and ~/.config/nvim might be options?

/usr/bin makes the most sense lets try it:

1mv nvim /usr/bin
44 | 2nvim
45 | 

Now, nvim was an installation directory, so it has the actual binary within. I had issues getting it to work unless I kept the nvim directory within /usr/bin.

/usr/bin normally has all of your system binaries, so having a directory in there feels a little off.

I ended up having to change /usr/bin/nvim to /usr/bin/nvim-dir and adding /usr/bin/nvim-dir/bin to my PATH as a solution.

Now we are on version 0.10.2 😀

Plugins

We gotta get some plugins installed. A little research and I found that package manager lazy.nvim.

I went ahead and followed the docs to setup some basic configuration I stashed in this repo.

That repo might change as I find my favorite custom config.

The LSP

At this point in the post, I swapped from vscode to neovim.

I think the plugin I am most concerned about getting up and running is my LSP support. I am going to do some research and see what I can find.

I found this page in the neovim docs. It looks like language servers are installed using a package manager.

I checked out this page where I see we can go through the process of install language severs one-by-one, but I think a better way might be out there.

I found this repo which is maintained and looks promising. We still manage installing servers, but this makes it easy to get them working in our projects.

I already have gopls installed, so I'll just try to get it working with that.

Adding this line to ~/.config/nvim/init.lua:

1require'lspconfig'.gopls.setup{}
46 | 

NvChad

After digging around with LSP details and a few other plugins, I decided it would be best to use a drop in configuration with good defaults. I decided to go with NvChad which has super easy installation instructions.

It has good defaults and I think it'll work for the time being.

I got it up and running and it is doing fine. I have not had any issues so far and was able to change over my theme.

Getting Around

I am going to spend a little bit of time learning how to getting around in neovim. I am already confident in things like insert mode. For selecting, I am just using the mouse. But I know select mode is a thing. Just going to dive in and look for the most useful things to know.

Here is an entry from Claude:

I'll share the most crucial Vim navigation skills that will dramatically improve your text editing efficiency:

Basic Movement Keys:

  • h, j, k, l are your fundamental movement keys
    • h: Move left
    • j: Move down
    • k: Move up
    • l: Move right

Word-Based Movement:

  • w: Move forward to the start of the next word
  • b: Move backward to the start of the previous word
  • e: Move to the end of the current word
  • W, B, E: Similar to w, b, e, but move by WORD (ignore punctuation)

Line Movement:

  • 0: Jump to the start of the line
  • $: Jump to the end of the line
  • ^: Jump to the first non-blank character of the line

Screen Movement:

  • Ctrl + f: Page down
  • Ctrl + b: Page up
  • H: Move to the top of the screen
  • M: Move to the middle of the screen
  • L: Move to the bottom of the screen

Advanced Movement:

  • gg: Go to the first line of the document
  • G: Go to the last line of the document
  • {number}G: Go to a specific line number

Search Movement:

  • /{pattern}: Search forward for a pattern
  • ?{pattern}: Search backward for a pattern
  • n: Repeat the last search forward
  • N: Repeat the last search backward

Quick Jumps:

  • (: Move to the start of the previous sentence
  • ): Move to the start of the next sentence
  • {: Move to the previous paragraph
  • }: Move to the next paragraph

Pro Tips:

  1. Combine movement with numbers for faster navigation (e.g., 5j moves 5 lines down)
  2. Use these movements with operators like d (delete) or c (change) for powerful editing
  3. Practice these until they become muscle memory

Bonus Tip: Use vimtutor in your terminal for an interactive tutorial that will help you master these skills quickly!

Would you like me to elaborate on any of these navigation techniques?

Find and Replace

Then, I questioned Calude about how to easily find and replace text within a document using nvim. Here is the response:

I'll cover find and replace in Vim, which is extremely powerful:

Basic Find and Replace:

  1. Within a single line:
  • s/old/new: Replace first occurrence
  • s/old/new/g: Replace all occurrences on current line
  1. Global Find and Replace:
  • :%s/old/new: Replace first occurrence in entire file
  • :%s/old/new/g: Replace ALL occurrences in entire file
  • :%s/old/new/gc: Replace with confirmation prompt before each replacement

Advanced Options:

  • :%s/old/new/gi: Case-insensitive replacement

  • :%s/old/new/gI: Case-sensitive replacement

  • `:%s/<old## Points of learning
    The following article touches on these technical skills:

  • installing binaries and moving them around on your system

  • learn the basics of vim motions

  • gain exposure to find and replace methods in nvim

  • install a precustomized nvim version, NvChad>/new/g`: Whole word replacement (matches 'old' exactly)

Regex Power:

  • Use regex for complex replacements
  • :%s/\v(pattern1)(pattern2)/\2\1/g: Swap patterns
  • :%s/\v(\d+)/number: \1/g: Add prefix to numbers

Special Characters:

  • \n: Newline in replacement
  • \r: Carriage return
  • \t: Tab

Scoped Replacements:

  • :10,20s/old/new/g: Replace between lines 10-20
  • :'<,'>s/old/new/g: Replace in visual selection

Pro Tips:

  • Always use /g to replace all occurrences
  • Use confirmation mode gc when making significant changes
  • Practice regex for complex replacements

Want me to elaborate on any of these techniques?

Combining Commands

I think reading this post on StackExchange really helped me to 'get the point' a little bit more. I was trying to taking this post and run it through AI for a spellcheck when I realized I didn't know how to do that in nvim.

So, in that post we see ggVG is the combination of commands to select all the text in a file. It doesn't feel intuitive, but man does it feel cool 😎.

I think my favorite commands are / and ? for searching forwards and backwards in a file. Instead of using my keyboard to move my cursor around the file, I notice it is way quicker to spot where you want to go, and then to search jump there using / or ? depending if you are moving forwards or backwards in the file based on your current cursor position.

I think this is where nvim shines. It's ability to allow you to quickly jump around a file without having to use the mouse is really the point.

For example, let's say I want to grab this current block of text. Mind you, this is off the dome, I did not reaseach this bit I am just rolling here.

Okay, that block right there ^, lets copy it real quick. I'm thinking we do ?For example to jump back in the file to the start of the block. Then we enter visual mode using v. Then we can run /here. to jump to the end of the line. Now that I say that out loud I know you can also run $ to go to the end of the line. Then you can yank the line using y.

So, you have the ability to on the fly combine commands to avoid using the mouse and that is an example of how we can do it. I think that is the point of nvim.

Conclusion

I do think getting around the document can be more effcient with nvim. I think it really boils down to taking the time to learn the mappings and then just forcing yourself to use the editor for a period of time. I think that is probably what I will commit to to give it a fair shake.

Setting things up can be time-consuming, but it is a good process if you are wanting to learn more about computers in general. For me, I don't think taking the time to get the most pristine customization is really where my time is best suited, so I opted in for a precustomized-install.

I did learn more about LSP's and it did make me interested in what it would look like to design my own LSP.

I was recently working on GTML and it has been the closest thing to make getting into compilers and interpreters. My lack of understanding of LSP's did limit my creative potential in that project.

NOTE TO SELF: Did you learn how LSPs work?

After

All my notes I take on this topic will go here.

Dropping Buffers

I found it was annoying when I was using telescope to open up new files in vim because tons of them would collect. You can run :bd to drop the current buffer. This is useful.

Shifting Blocks of Text

I noticed it you could not indent blocks of text by using tab. So, I did a little research and learned you can use SELECT mode to highlight a block of text and then use < or > to shift things.

Changing the Current Word

I always find myself doing CRTL+BACKSPACE to delete words in vscode. I learned you can run Bciw to change the inner word which is a vim command which more or less does the same thing.

Rust Analyzer

I found it was a little bit difficult to get rust-analyzer working with NvChad as it did not come preinstalled. However, I was able to get it up and running and here is the page where I found my solution.

Commenting Out Text

I discovered I could not use CTRL+/ to comment out a highlighted section of text. NvChad comes with the ability to use gc to comment out blocks of text instead.

Jumping to End of Line in Insert Mode

Bruh, A brings you to the end of the line in insert mode. I've been doing $i my gosh.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

-------------------------------------------------------------------------------- /post/random-numbers-in-rust.html: -------------------------------------------------------------------------------- 1 | phillip england
16 |

[random numbers in rust]

written 12/15/2024

for i in range 0..?

30 |

Generating Random Numbers

I asked Calude how to generate random numbers in Rust. Here is the response:

I'll show you how to generate random numbers in Rust using the rand crate, which is the most common way to generate random numbers in Rust.
First, you'll need to add the rand crate to your Cargo.toml file:

1[dependencies]
31 | 2rand = "0.8.5"
32 | 

Here are different ways to generate random numbers:

  1. Generating a Random Integer
1use rand::Rng;
33 | 2
34 | 3fn main() {
35 | 4    let mut rng = rand::thread_rng();
36 | 5    
37 | 6    // Random integer between 1 and 10
38 | 7    let random_number = rng.gen_range(1..=10);
39 | 8    println!("Random number: {}", random_number);
40 | 9}
41 | 
  1. Generating a Random Float
 1use rand::Rng;
42 |  2
43 |  3fn main() {
44 |  4    let mut rng = rand::thread_rng();
45 |  5    
46 |  6    // Random float between 0.0 and 1.0
47 |  7    let random_float = rng.gen::<f64>();
48 |  8    println!("Random float: {}", random_float);
49 |  9    
50 | 10    // Random float in a specific range
51 | 11    let random_range_float = rng.gen_range(0.0..10.0);
52 | 12    println!("Random float in range: {}", random_range_float);
53 | 13}
54 | 
  1. Random Boolean
1use rand::Rng;
55 | 2
56 | 3fn main() {
57 | 4    let mut rng = rand::thread_rng();
58 | 5    
59 | 6    let random_bool = rng.gen_bool(0.5); // 50% true, 50% false
60 | 7    println!("Random boolean: {}", random_bool);
61 | 8}
62 | 
  1. Random Choice from a Collection
 1use rand::seq::SliceRandom;
63 |  2
64 |  3fn main() {
65 |  4    let mut rng = rand::thread_rng();
66 |  5    
67 |  6    let choices = vec![1, 2, 3, 4, 5];
68 |  7    
69 |  8    // Random element
70 |  9    if let Some(chosen) = choices.choose(&mut rng) {
71 | 10        println!("Randomly chosen: {}", chosen);
72 | 11    }
73 | 12    
74 | 13    // Shuffling a vector
75 | 14    let mut nums = vec![1, 2, 3, 4, 5];
76 | 15    nums.shuffle(&mut rng);
77 | 16    println!("Shuffled vector: {:?}", nums);
78 | 17}
79 | 
  1. Random Distribution (Noraml/Gaussian)
 1use rand::distributions::{Normal, Distribution};
80 |  2
81 |  3fn main() {
82 |  4    let mut rng = rand::thread_rng();
83 |  5    
84 |  6    // Normal distribution with mean 0.0 and standard deviation 1.0
85 |  7    let normal = Normal::new(0.0, 1.0).unwrap();
86 |  8    let random_gaussian = normal.sample(&mut rng);
87 |  9    println!("Gaussian distributed number: {}", random_gaussian);
88 | 10}
89 | 

Key points to remember:

  • rand::thread_rng() creates a random number generator local to the current thread
  • gen_range() lets you specify a range (inclusive or exclusive)
  • gen() generates a random number of the specified type
  • Always import rand::Rng to use these methods

Each time you run the program, you'll get different random numbers. The rand crate provides a robust and secure way to generate random numbers in Rust.

"If you don't fight for your love, what kind of love do you have" - Keanu Reeves

--------------------------------------------------------------------------------