mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-10 04:22:06 +00:00
chore: added coderpad sponsorship and blog post
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"useTabs": true
|
"useTabs": true,
|
||||||
|
"endOfLine": "auto"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ This repository acts as the source code location for [the Unicorn Utterances blo
|
|||||||
|
|
||||||
<a href="https://www.thepolyglotdeveloper.com/" target="_blank" rel="noopener noreferrer sponsored"><img alt="The Polyglot Developer" src="https://unicorn-utterances.com/sponsors/the-polyglot-developer.svg" width="300"/></a>
|
<a href="https://www.thepolyglotdeveloper.com/" target="_blank" rel="noopener noreferrer sponsored"><img alt="The Polyglot Developer" src="https://unicorn-utterances.com/sponsors/the-polyglot-developer.svg" width="300"/></a>
|
||||||
<a href="https://oceanbit.dev/" target="_blank" rel="noopener noreferrer sponsored"><img alt="OceanBit" src="https://unicorn-utterances.com/sponsors/oceanbit.svg" width="300"/></a>
|
<a href="https://oceanbit.dev/" target="_blank" rel="noopener noreferrer sponsored"><img alt="OceanBit" src="https://unicorn-utterances.com/sponsors/oceanbit.svg" width="300"/></a>
|
||||||
|
<a href="https://coderpad.io/" target="_blank" rel="noopener noreferrer sponsored"><img alt="CoderPad" src="https://unicorn-utterances.com/sponsors/coderpad.svg" width="300"/></a>
|
||||||
|
|
||||||
## Statement of Ethics
|
## Statement of Ethics
|
||||||
|
|
||||||
|
|||||||
511
content/blog/rust-enums-matching-options-api/index.md
Normal file
511
content/blog/rust-enums-matching-options-api/index.md
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "Rust Enums, Matching, & Options API",
|
||||||
|
description: "",
|
||||||
|
published: '2021-04-16T22:12:03.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['rust'],
|
||||||
|
attached: [],
|
||||||
|
license: 'coderpad',
|
||||||
|
originalLink: 'https://coderpad.io/blog/rust-enums-matching-options-api/'
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
If you’ve been active in the programming community within the past few years, you’ve undoubtedly heard of [Rust](https://www.rust-lang.org/). Its technical foundation and vibrant community have proven themselves to be a good benchmark for quick language growth.
|
||||||
|
|
||||||
|
But what does Rust do that has garnered such a positive response from the community? Not only does Rust provide a great deal of memory safety (something that’s rare in low-level languages in the past), but also includes powerful features that make development much nicer.
|
||||||
|
|
||||||
|
One of the many features that highlights Rust’s capabilities is its handling of enums and matching.
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
Like many languages with strict typings, Rust [has an enum feature](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html). To declare an enum is simple enough, start with `pub enum` and name the values.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum CodeLang {
|
||||||
|
Rust,
|
||||||
|
JavaScript,
|
||||||
|
Swift,
|
||||||
|
Kotlin,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a variable with the type of that enum, you can use the name of the enum with the value:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let lang = CodeLang::Rust;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Likewise, you can use the `enum` as a type in places like function params. Let’s say that you want to detect which version of a programming language supported in CoderPad. We’ll start by hard-coding the version of Rust:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version(_lang: CodeLang) -> &'static str {
|
||||||
|
return "1.46";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
While this code *works*, it’s not very functional. If you pass in “CodeLang::JavaScript”, the version number isn’t correct. Let’s take a look at how we can fix that in the next section.
|
||||||
|
|
||||||
|
## Matching
|
||||||
|
|
||||||
|
While you *could* use `if` statements to detect which enum is passed in, like so:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version(lang: CodeLang) -> &'static str {
|
||||||
|
if let CodeLang::Rust = lang {
|
||||||
|
return "1.46";
|
||||||
|
}
|
||||||
|
|
||||||
|
if let CodeLang::JavaScript = lang {
|
||||||
|
return "2021";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lang = CodeLang::Rust;
|
||||||
|
|
||||||
|
let ver = get_version(lang);
|
||||||
|
|
||||||
|
println!("Version {}", ver);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This easily becomes unwieldy when dealing with more than one or two values in the enum. This is where Rust’s `match` operator comes into play. Let’s match the variable with all of the existing values in the enum:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version(lang: CodeLang) -> &'static str {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => "1.46",
|
||||||
|
CodeLang::JavaScript => "2021",
|
||||||
|
CodeLang::Swift => "5.3",
|
||||||
|
CodeLang::Python => "3.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you’re familiar with a programming language that has a feature similar to “[switch/case](https://www.tutorialspoint.com/cprogramming/switch_statement_in_c.htm)”, this example is a close approximation of that functionality. However, as you’ll soon see, `match` in Rust is significantly more powerful than most implementations of switch/case.
|
||||||
|
|
||||||
|
## Pattern Matching
|
||||||
|
|
||||||
|
While most implementations of `switch/case` only allow simple primitives matching, such as strings or numbers, Rust’s `match` allows you to have more granular control over what is matched and how. For example, you can match anything that isn’t matched otherwise using the `_` identifier:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version(lang: CodeLang) -> &'static str {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => "1.46",
|
||||||
|
_ => "Unknown version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You are also able to match more than a single value at a time. In this example, we’re doing a check on versions for more than one programming language at a time.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version<'a>(lang: CodeLang, other_lang: CodeLang) -> (&'a str, &'a str) {
|
||||||
|
match (lang, other_lang) {
|
||||||
|
(CodeLang::Rust, CodeLang::Python) => ("1.46", "3.8"),
|
||||||
|
_ => ("Unknown", "Unknown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows some of the power of `match` . However, there’s more that you’re able to do with enums.
|
||||||
|
|
||||||
|
## Value Storage
|
||||||
|
|
||||||
|
Not only are enums values within themselves, but you can also store values within enums to be accessed later.
|
||||||
|
|
||||||
|
For example, CoderPad supports two different versions of Python. However, instead of creating a `CodeLang::Python` and `CoderLang::Python2` enum values, we can use one value and store the major version within.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub enum CodeLang {
|
||||||
|
Rust,
|
||||||
|
JavaScript,
|
||||||
|
Swift,
|
||||||
|
Python(u8),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let python2 = CodeLang::Python(2);
|
||||||
|
|
||||||
|
let pythonVer = get_version(python2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
We’re able to expand our `if let` expression from before to access the value within:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
if let CodeLang::Python(ver) = python2 {
|
||||||
|
println!("Python version is {}", ver);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, just as before, we’re able to leverage `match` to unpack the value within the enum:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version(lang: CodeLang) -> &'static str {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => "1.46",
|
||||||
|
CodeLang::JavaScript => "2021",
|
||||||
|
CodeLang::Python(ver) => {
|
||||||
|
if ver == 3 { "3.8" } else { "2.7" }
|
||||||
|
},
|
||||||
|
_ => "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Not all enums need to be manually set, however! Rust has some enums built-in to the language, ready for use.
|
||||||
|
|
||||||
|
## Option Enum
|
||||||
|
|
||||||
|
While we’re currently returning the string `”Unknown”` as a version, that’s not ideal. Namely, we’d have to do a string comparison to check if we’re returning a known version or not, rather than having a value dedicated to a lack of value.
|
||||||
|
|
||||||
|
This is where Rust’s `Option` enum comes into play. `Option<T>` describes a data type that either has `Some(data)` or `None` to speak of.
|
||||||
|
|
||||||
|
For example, we can rewrite the above function to:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => Some("1.46"),
|
||||||
|
CodeLang::JavaScript => Some("2021"),
|
||||||
|
CodeLang::Python(ver) => {
|
||||||
|
if ver == 3 { Some("3.8") } else { Some("2.7") }
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By doing this, we can make our logic more representative and check if a value is `None`
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let swift_version = get_version(CodeLang::Swift);
|
||||||
|
|
||||||
|
if let None = swift_version {
|
||||||
|
println!("We could not find a valid version of your tool");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, we can of course use `match` to migrate from an `if` to check when values are set:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let code_version = get_version(CodeLang::Rust);
|
||||||
|
|
||||||
|
match code_version {
|
||||||
|
Some(val) => {
|
||||||
|
println!("Your version is {}", val);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("We could not find a valid version of your tool");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operators
|
||||||
|
|
||||||
|
While the above code functions as intended, if we add more conditional logic, we may find ourselves wanting to make abstractions. Let’s look at some of these abstractions Rust provides for us
|
||||||
|
|
||||||
|
### Map Operator
|
||||||
|
|
||||||
|
What if we wanted to convert `rust_version` to a string, but wanted to handle edge-cases where `None` was present.
|
||||||
|
|
||||||
|
You might write something like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let rust_version = get_version(CodeLang::Rust);
|
||||||
|
|
||||||
|
let version_str = match rust_version {
|
||||||
|
Some(val) => {
|
||||||
|
Some(format!("Your version is {}", val))
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(val) = version_str {
|
||||||
|
println!("{}", val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This `match` of taking `Some` and mapping it to a new value and leaving `None` s to resolve as `None` still is baked into the Option enum as a method called `.map` :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let rust_version = get_version(CodeLang::Rust);
|
||||||
|
|
||||||
|
let version_str = rust_version.map(|val| {
|
||||||
|
format!("Your version is {}", val)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(val) = version_str {
|
||||||
|
println!("{}", val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
How close is the implementation of `.map` to what we were doing before? Let’s take a look at [Rust’s source code implementation of `.map` ](https://github.com/rust-lang/rust/blob/8dc0ae24bcafeb52259ae20fcad29185acf31fcc/library/core/src/option.rs#L485-L490):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
|
||||||
|
match self {
|
||||||
|
Some(x) => Some(f(x)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, we matched our implementation very similarly, matching `Some` to another `Some` and `None` to another `None`
|
||||||
|
|
||||||
|
### And Then Operator
|
||||||
|
|
||||||
|
While the automatic wrapping of the `.map` function return value into a `Some` can be useful in most instances, there may be times where you want to conditionally make something inside the `map`
|
||||||
|
|
||||||
|
Let’s say that we only want version numbers that contain a dot (indicating there’s a minor version). We could do something like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let rust_version = get_version(CodeLang::JavaScript);
|
||||||
|
|
||||||
|
let version_str = match rust_version {
|
||||||
|
Some(val) => {
|
||||||
|
if val.contains(".") {
|
||||||
|
Some(format!("Your version is {}", val))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(val) = version_str {
|
||||||
|
println!("{}", val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Which we can rewrite using Rust’s `and_then` operator:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let rust_version = get_version(CodeLang::JavaScript);
|
||||||
|
|
||||||
|
let version_str = rust_version.and_then(|val| {
|
||||||
|
if val.contains(".") {
|
||||||
|
Some(format!("Your version is {}", val))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(val) = version_str {
|
||||||
|
println!("{}", val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we look at [Rust’s source code for the operator](https://github.com/rust-lang/rust/blob/8dc0ae24bcafeb52259ae20fcad29185acf31fcc/library/core/src/option.rs#L722-L727), we can see the similarity to the `.map` implementation, simply without wrapping `fn` in `Some` :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn and_then<U, F: FnOnce(T) -> Option<U>>(self, f: F) -> Option<U> {
|
||||||
|
match self {
|
||||||
|
Some(x) => f(x),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Putting it Together
|
||||||
|
|
||||||
|
Now that we’re familiar with the Option enum, operators, and pattern matching let’s put it all together!
|
||||||
|
|
||||||
|
Let’s start with the same `get_version` function baseline we’ve been using for a few examples:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub enum CodeLang {
|
||||||
|
Rust,
|
||||||
|
JavaScript,
|
||||||
|
Swift,
|
||||||
|
Python(u8),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => Some("1.46"),
|
||||||
|
CodeLang::JavaScript => Some("2021"),
|
||||||
|
CodeLang::Python(ver) => {
|
||||||
|
if ver == 3 { Some("3.8") } else { Some("2.7") }
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lang = CodeLang::JavaScript;
|
||||||
|
|
||||||
|
let lang_version = get_version(lang);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Given this baseline, let’s build a semver checker. Given a coding language, tell us what the major and minor versions of that language are.
|
||||||
|
|
||||||
|
For example, Rust (1.46) would return “**Major: 1. Minor: 46**”, while JavaScript (2021) would return **“Major: 2021. Minor: 0**”
|
||||||
|
|
||||||
|
We’ll do this check using a Regex that parses any dots in the version string.
|
||||||
|
|
||||||
|
```
|
||||||
|
(\d+)(?:\.(\d+))?
|
||||||
|
```
|
||||||
|
|
||||||
|
This regex will match the first capture group as anything before the first period, then optionally provide a second capture if there is a period, matching anything after that period. Let’s add that Regex and the captures in our `main` function:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let version_regex = Regex::new(r"(\d+)(?:\.(\d+))?").unwrap();
|
||||||
|
|
||||||
|
let version_matches = lang_version.and_then(|version_str| {
|
||||||
|
return version_regex.captures(version_str);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
In the code sample above, we’re using `and_then` in order to flatten `captures` into a single-layer `Option` enum - seeing as `lang_version` is an Option itself and `captures` returns an Option as well.
|
||||||
|
|
||||||
|
While `.captures` sounds like it should return an array of the capture strings, in reality it returns [a structure with various methods and properties](https://docs.rs/regex/1.1.9/regex/struct.Captures.html). To get the strings for each of these values, we’ll use `version_matches.map` to get both of these capture group strings:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let major_minor_captures = version_matches
|
||||||
|
.map(|caps| {
|
||||||
|
(
|
||||||
|
caps.get(1).map(|m| m.as_str()),
|
||||||
|
caps.get(2).map(|m| m.as_str()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
While we’d expect capture group 1 to always provide a value (given our input), we’d see “None” returned in capture group 2 if there’s no period (like with JavaScript’s version number of “2021”). Because of this, there are instances where `caps.get(2)` may be `None` . As such, we want to make sure to get a `0` in the place of `None` and convert the `Some<&str>, Option<&str>` into `Some<&str, &str>` . To do this, we’ll use `and_then` and a `match` :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let major_minor = major_minor_captures
|
||||||
|
.and_then(|(first_opt, second_opt)| {
|
||||||
|
match (first_opt, second_opt) {
|
||||||
|
(Some(major), Some(minor)) => Some((major, minor)),
|
||||||
|
(Some(major), None) => Some((major, "0")),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, we can use an `if let` to deconstruct the values and print the major and minor versions:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
if let Some((first, second)) = major_minor {
|
||||||
|
println!("Major: {}. Minor: {}", first, second);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The final version of the project should look something like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub enum CodeLang {
|
||||||
|
Rust,
|
||||||
|
JavaScript,
|
||||||
|
Swift,
|
||||||
|
Python(u8),
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version<'a>(lang: CodeLang) -> Option<&'a str> {
|
||||||
|
match lang {
|
||||||
|
CodeLang::Rust => Some("1.46"),
|
||||||
|
CodeLang::JavaScript => Some("2021"),
|
||||||
|
CodeLang::Python(ver) => {
|
||||||
|
if ver == 3 { Some("3.8") } else { Some("2.7") }
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lang = CodeLang::JavaScript;
|
||||||
|
|
||||||
|
let lang_version = get_version(lang);
|
||||||
|
|
||||||
|
let version_regex = Regex::new(r"(\d+)(?:\.(\d+))?").unwrap();
|
||||||
|
|
||||||
|
let version_matches = lang_version.and_then(|version_str| {
|
||||||
|
return version_regex.captures(version_str);
|
||||||
|
});
|
||||||
|
|
||||||
|
let major_minor_captures = version_matches
|
||||||
|
.map(|caps| {
|
||||||
|
(
|
||||||
|
caps.get(1).map(|m| m.as_str()),
|
||||||
|
caps.get(2).map(|m| m.as_str()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let major_minor = major_minor_captures
|
||||||
|
.and_then(|(first_opt, second_opt)| {
|
||||||
|
match (first_opt, second_opt) {
|
||||||
|
(Some(major), Some(minor)) => Some((major, minor)),
|
||||||
|
(Some(major), None) => Some((major, "0")),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if let Some((first, second)) = major_minor {
|
||||||
|
println!("Major: {}. Minor: {}", first, second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion & Challenge
|
||||||
|
|
||||||
|
All of these features are used regularly in Rust applications: enums, matching, option operators. We hope that you can take these features and utilize them in your applications along your journey to learn Rust.
|
||||||
|
|
||||||
|
Let’s close with a challenge. If you get stuck anywhere along the way or have comments/questions about this article, you can join our[ public chat community where we talk about general coding topics as well as interviewing](http://bit.ly/coderpad-slack).
|
||||||
|
|
||||||
|
|
||||||
|
Let’s say that we have the “patch” version of a software tracked. We want to expand the logic of our code to support checking “5.1.2” and return “2” as the “patch” version. Given the modified regex to support three optional capture groups:
|
||||||
|
|
||||||
|
```
|
||||||
|
(\d+)(?:\.(\d+))?(?:\.(\d+))?
|
||||||
|
```
|
||||||
|
|
||||||
|
How can you modify the code below to support the match version being listed out properly?
|
||||||
|
|
||||||
|
<iframe src="https://app.coderpad.io/sandbox?question_id=175664" width="640" height="480" loading="lazy"></iframe>
|
||||||
|
|
||||||
|
You’ll know the code is working when you’re able to output the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
Major: 2021. Minor: 0, Patch: 0
|
||||||
|
Major: 1. Minor: 46, Patch: 0
|
||||||
|
Major: 5. Minor: 1, Patch: 2
|
||||||
|
```
|
||||||
@@ -30,5 +30,13 @@
|
|||||||
"explainLink": "https://creativecommons.org/licenses/by-nc-nd/4.0/",
|
"explainLink": "https://creativecommons.org/licenses/by-nc-nd/4.0/",
|
||||||
"name": "Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)",
|
"name": "Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)",
|
||||||
"displayName": "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) License"
|
"displayName": "Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) License"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "coderpad",
|
||||||
|
"footerImg": "/coderpad.png",
|
||||||
|
"licenceType": "Owned by CoderPad",
|
||||||
|
"explainLink": "https://coderpad.io/about-us/",
|
||||||
|
"name": "Written for CoderPad, reposted to Unicorn Utterances",
|
||||||
|
"displayName": "Written for CoderPad"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ As mentioned previously, we also have a Discord where we chat tech, help out wit
|
|||||||
">
|
">
|
||||||
<a href="https://oceanbit.dev/" target="_blank" rel="noopener noreferrer sponsored"><img alt="OceanBit" src="/sponsors/oceanbit.svg" style="width: 300px; margin: 0"/></a>
|
<a href="https://oceanbit.dev/" target="_blank" rel="noopener noreferrer sponsored"><img alt="OceanBit" src="/sponsors/oceanbit.svg" style="width: 300px; margin: 0"/></a>
|
||||||
</li>
|
</li>
|
||||||
|
<li style="
|
||||||
|
margin: 16px;
|
||||||
|
">
|
||||||
|
<a href="https://coderpad.io/" target="_blank" rel="noopener noreferrer sponsored"><img alt="CoderPad" src="/sponsors/coderpad.svg" style="width: 300px; margin: 0"/></a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
## Statement of Ethics {#ethics}
|
## Statement of Ethics {#ethics}
|
||||||
|
|||||||
BIN
static/coderpad.png
Normal file
BIN
static/coderpad.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
14
static/sponsors/coderpad.svg
Normal file
14
static/sponsors/coderpad.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="475" height="67" viewBox="0 0 475 67" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M474.76 65.1389L456.947 65.8479L456.149 61.328C452.693 64.7844 448.35 66.6455 443.387 66.6455C437.538 66.6455 433.284 64.6958 430.271 60.6191C427.346 56.631 426.017 51.1363 426.017 43.8691C426.017 35.8929 427.967 29.7778 432.044 25.4352C436.032 21.1812 441.969 18.877 449.946 18.877C451.807 18.877 453.668 19.0542 455.352 19.6746V9.03967L448.616 7.44444V0.708984L464.391 0L465.987 1.86109L467.582 3.27909V56.4537L474.406 58.049L474.76 65.1389ZM455.44 51.8453V30.3095C453.934 29.5119 451.984 28.9802 448.794 28.9802C442.324 28.9802 438.956 33.7659 438.956 43.3373C438.956 52.377 441.615 56.8082 447.11 56.8082C448.705 56.8082 450.3 56.5423 451.364 55.4788C452.87 54.6812 453.756 53.8836 454.288 53.086L455.44 51.8453Z" fill="#CC4E41"/>
|
||||||
|
<path d="M114.237 16.9272C111.578 15.8637 108.388 15.5979 104.754 15.5979C95.4484 15.5979 90.7513 22.3333 90.7513 36.336C90.7513 49.2751 95.537 55.7447 105.286 55.7447C107.767 55.7447 110.426 55.213 112.819 54.2381C115.478 53.1746 117.604 52.2884 119.022 51.3135L120.972 49.8955L125.581 57.6058C125.492 57.6945 124.872 58.3148 124.694 58.6693C124.251 58.9352 123.188 59.8214 121.77 61.0622C120.263 62.1257 118.579 63.0119 117.161 63.8095C115.566 64.6072 113.439 65.3161 111.046 65.9365C108.565 66.6455 106.261 67 103.779 67C95.0939 67 88.5357 64.3413 83.75 59.2897C79.1415 54.0608 76.66 46.705 76.66 36.9563C76.66 27.119 79.3188 19.4087 84.459 13.7368C89.5992 8.15343 96.4233 5.22882 105.374 5.22882C108.299 5.22882 111.578 5.49468 114.769 6.2923C117.959 7.08992 120.618 7.62167 122.39 8.24204L125.049 9.30555L124.34 25.2579H116.098L114.237 16.9272Z" fill="#515050"/>
|
||||||
|
<path d="M154.384 19.3201C161.119 19.3201 166.348 21.2698 170.247 25.3465C173.969 29.3346 175.919 34.918 175.919 42.0966C175.919 49.8069 173.969 55.4788 169.804 59.9987C165.639 64.5185 160.233 66.4683 153.32 66.4683C139.14 66.4683 131.784 58.7579 131.784 43.2487C131.784 35.5384 133.734 29.8664 137.899 25.7011C141.622 21.2698 147.471 19.3201 154.384 19.3201ZM153.852 28.8915C150.661 28.8915 148.091 29.955 146.762 32.082C145.432 34.209 144.369 37.6653 144.369 42.8942C144.369 47.6799 145.078 51.4021 146.319 53.5291C147.825 55.922 150.041 56.8968 153.143 56.8968C156.333 56.8968 158.46 55.8333 159.878 53.5291C161.385 51.4021 162.005 47.5026 162.005 42.6283C162.005 38.0198 161.296 34.6521 160.055 32.5251C158.992 30.0436 156.865 28.8915 153.852 28.8915Z" fill="#515050"/>
|
||||||
|
<path d="M229.98 65.1389L212.167 65.8479L211.458 61.328C208.001 64.7844 203.57 66.6455 198.696 66.6455C192.846 66.6455 188.593 64.6958 185.579 60.6191C182.655 56.631 181.325 51.1363 181.325 43.8691C181.325 35.8929 183.275 29.7778 187.352 25.4352C191.34 21.1812 197.278 18.877 205.254 18.877C207.115 18.877 208.976 19.0542 210.66 19.6746V9.03967L203.925 7.44444V0.708984L219.877 0L221.738 1.86109L223.245 3.27909V56.4537L229.98 58.049V65.1389ZM210.571 51.8453V30.3095C209.065 29.5119 206.849 28.9802 204.013 28.9802C197.544 28.9802 194.176 33.7659 194.176 43.3373C194.176 52.377 196.835 56.8082 202.241 56.8082C203.747 56.8082 205.431 56.5423 206.761 55.4788C208.179 54.6812 209.153 53.8836 209.685 53.086L210.571 51.8453Z" fill="#515050"/>
|
||||||
|
<path d="M275.71 45.3756L247.616 45.6415C247.794 49.0979 248.946 52.0225 250.807 53.795C252.668 55.6561 254.884 56.7196 257.72 56.7196C259.669 56.7196 261.974 56.4537 264.366 55.6561C266.759 54.8585 268.62 54.3267 269.773 53.7063L271.722 52.6429L275.444 59.3783C274.735 59.9101 274.026 60.619 272.786 61.2394C271.634 62.037 269.329 63.1005 266.05 64.4299C262.594 65.7593 259.403 66.3796 256.213 66.3796C249.034 66.3796 243.628 64.4299 239.729 60.3532C236.007 56.3651 234.057 50.7817 234.057 43.6032C234.057 36.1587 236.007 30.1323 240.083 25.7011C244.071 21.4471 249.655 19.0542 256.745 19.0542C263.037 19.0542 267.646 20.9153 271.013 24.3717C274.381 27.828 276.065 32.8796 276.065 38.9947L275.71 45.3756ZM256.036 28.9801C253.643 28.9801 251.959 29.7778 250.364 31.373C248.769 32.9682 247.971 35.0952 247.705 37.6653H262.86C262.86 32.082 260.733 28.9801 256.036 28.9801Z" fill="#515050"/>
|
||||||
|
<path d="M281.471 20.7381L299.019 20.0291L299.728 25.8783C299.993 25.3466 300.702 24.6376 301.589 24.0172C302.475 23.3082 303.982 22.1561 305.843 21.0926C307.792 19.9405 309.831 19.2315 311.515 19.2315C312.844 19.2315 314.173 19.3201 315.503 19.586C316.832 19.8518 317.896 20.1177 318.339 20.3836L319.136 20.6495L318.427 36.7791H311.16L309.831 29.5119C308.413 29.5119 306.906 29.8664 305.311 30.664C303.716 31.4616 302.652 32.1706 301.855 32.9683L300.702 34.209V56.8082L308.767 58.758V65.4934H281.471V58.758L288.295 56.8082V29.1574L281.471 27.5622V20.7381Z" fill="#515050"/>
|
||||||
|
<path d="M349.534 58.2262V65.4934H323.745V58.2262L330.037 56.2765V16.041L323.745 13.5595V6.64682L343.154 6.2923H351.307C357.776 6.2923 362.739 7.71031 366.462 11.078C370.184 14.2685 371.956 18.5225 371.956 24.2831C371.956 31.1958 370.007 36.5132 365.575 40.0582C361.41 43.5145 355.472 45.3756 347.673 45.3756H343.065V56.5423L349.534 58.2262ZM342.976 16.1296V35.0066H347.496C350.952 35.0066 353.877 34.209 355.65 32.5251C357.511 30.9299 358.486 28.537 358.486 25.6124C358.486 22.7764 357.688 20.3836 356.093 18.7883C354.497 17.1931 352.37 16.1296 349.623 16.1296H342.976Z" fill="#CC4E41"/>
|
||||||
|
<path d="M421.586 65.1389L404.836 65.8479L403.418 60.4418C402.886 60.7077 402.177 61.5053 401.468 62.3029C400.671 63.1005 398.898 63.8982 396.417 65.1389C393.935 66.291 391.808 67 389.593 67C385.516 67 382.325 65.8479 380.021 63.5437C377.628 61.2394 376.565 57.9603 376.565 53.7063C376.565 44.4008 383.389 39.7037 396.948 39.7037H402.798V35.0952C402.798 30.8413 400.671 28.8029 396.328 28.8029C394.999 28.8029 393.758 28.9802 392.429 29.0688C391.099 29.246 390.302 29.4233 389.681 29.7778L388.884 29.955L387.554 35.3611H379.933L379.224 22.7764C386.048 20.3836 392.251 19.3201 397.569 19.3201C404.038 19.3201 408.47 20.6495 411.217 23.0423C413.876 25.4352 415.205 29.7778 415.205 35.9815V56.9855L421.497 58.4034L421.586 65.1389ZM388.706 52.0225C388.706 55.4788 390.302 57.2513 393.758 57.2513C395.087 57.2513 396.417 56.8968 397.923 55.922C399.341 54.8585 400.582 54.3267 401.38 53.3518L402.709 52.1997V46.6164H397.923C394.556 46.6164 392.163 46.9709 391.011 47.9458C389.415 48.6548 388.706 50.1614 388.706 52.0225Z" fill="#CC4E41"/>
|
||||||
|
<path d="M56.8082 2.48148H50.1614H4.96297C2.12699 2.48148 0 4.60847 0 7.44445V52.2884V59.1124C0 61.9484 2.12699 64.0754 4.96297 64.0754H11.6098H56.8082C59.5556 64.0754 61.7712 61.9484 61.7712 59.1124V14.2685V7.44445C61.7712 4.60847 59.6442 2.48148 56.8082 2.48148Z" fill="#CC4E41"/>
|
||||||
|
<path d="M12.5848 34.1204L22.5993 37.6654V42.6283L7.44458 36.2474V32.082L22.5993 25.7011V30.664L12.5848 34.1204Z" fill="white"/>
|
||||||
|
<path d="M28.3599 47.1481H24.6377L34.0319 19.4087H37.8427L28.3599 47.1481Z" fill="white"/>
|
||||||
|
<path d="M49.0979 34.1204L38.9061 30.5754V25.7011L54.1495 32.082V36.2474L38.9061 42.6283V37.754L49.0979 34.1204Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.8 KiB |
Reference in New Issue
Block a user