mirror of
https://github.com/bootdotdev/fcc-learn-golang-assets.git
synced 2025-12-13 16:51:17 +00:00
first
This commit is contained in:
111
course/15-generics/exercises/3-constraints/code.go
Normal file
111
course/15-generics/exercises/3-constraints/code.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func chargeForLineItem[T lineItem](newItem T, oldItems []T, balance float64) ([]T, float64, error) {
|
||||
// ?
|
||||
}
|
||||
|
||||
// don't edit below this line
|
||||
|
||||
type lineItem interface {
|
||||
GetCost() float64
|
||||
GetName() string
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
userEmail string
|
||||
startDate time.Time
|
||||
interval string
|
||||
}
|
||||
|
||||
func (s subscription) GetName() string {
|
||||
return fmt.Sprintf("%s subscription", s.interval)
|
||||
}
|
||||
|
||||
func (s subscription) GetCost() float64 {
|
||||
if s.interval == "monthly" {
|
||||
return 25.00
|
||||
}
|
||||
if s.interval == "yearly" {
|
||||
return 250.00
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
type oneTimeUsagePlan struct {
|
||||
userEmail string
|
||||
numEmailsAllowed int
|
||||
}
|
||||
|
||||
func (otup oneTimeUsagePlan) GetName() string {
|
||||
return fmt.Sprintf("one time usage plan with %v emails", otup.numEmailsAllowed)
|
||||
}
|
||||
|
||||
func (otup oneTimeUsagePlan) GetCost() float64 {
|
||||
const costPerEmail = 0.03
|
||||
return float64(otup.numEmailsAllowed) * costPerEmail
|
||||
}
|
||||
|
||||
func main() {
|
||||
test(subscription{
|
||||
userEmail: "john@example.com",
|
||||
startDate: time.Now().UTC(),
|
||||
interval: "yearly",
|
||||
},
|
||||
[]subscription{},
|
||||
1000.00,
|
||||
)
|
||||
test(subscription{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC(),
|
||||
interval: "monthly",
|
||||
},
|
||||
[]subscription{
|
||||
{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC().Add(-time.Hour * 24 * 7),
|
||||
interval: "monthly",
|
||||
},
|
||||
{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC().Add(-time.Hour * 24 * 7 * 52 * 2),
|
||||
interval: "yearly",
|
||||
},
|
||||
},
|
||||
686.20,
|
||||
)
|
||||
test(oneTimeUsagePlan{
|
||||
userEmail: "dillon@example.com",
|
||||
numEmailsAllowed: 5000,
|
||||
},
|
||||
[]oneTimeUsagePlan{},
|
||||
756.20,
|
||||
)
|
||||
test(oneTimeUsagePlan{
|
||||
userEmail: "dalton@example.com",
|
||||
numEmailsAllowed: 100000,
|
||||
},
|
||||
[]oneTimeUsagePlan{
|
||||
{
|
||||
userEmail: "dalton@example.com",
|
||||
numEmailsAllowed: 34200,
|
||||
},
|
||||
},
|
||||
32.20,
|
||||
)
|
||||
}
|
||||
|
||||
func test[T lineItem](newItem T, oldItems []T, balance float64) {
|
||||
fmt.Println(" --- ")
|
||||
fmt.Printf("Charging customer for a '%s', current balance is %v...\n", newItem.GetName(), balance)
|
||||
newItems, newBalance, err := chargeForLineItem(newItem, oldItems, balance)
|
||||
if err != nil {
|
||||
fmt.Printf("Got error: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("New balance is: %v. Total number of line items is now %v\n", newBalance, len(newItems))
|
||||
}
|
||||
117
course/15-generics/exercises/3-constraints/complete.go
Normal file
117
course/15-generics/exercises/3-constraints/complete.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func chargeForLineItem[T lineItem](newItem T, oldItems []T, balance float64) ([]T, float64, error) {
|
||||
newBalance := balance - newItem.GetCost()
|
||||
if newBalance < 0 {
|
||||
return nil, 0, errors.New("insufficient funds")
|
||||
}
|
||||
oldItems = append(oldItems, newItem)
|
||||
return oldItems, newBalance, nil
|
||||
}
|
||||
|
||||
// don't edit below this line
|
||||
|
||||
type lineItem interface {
|
||||
GetCost() float64
|
||||
GetName() string
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
userEmail string
|
||||
startDate time.Time
|
||||
interval string
|
||||
}
|
||||
|
||||
func (s subscription) GetName() string {
|
||||
return fmt.Sprintf("%s subscription", s.interval)
|
||||
}
|
||||
|
||||
func (s subscription) GetCost() float64 {
|
||||
if s.interval == "monthly" {
|
||||
return 25.00
|
||||
}
|
||||
if s.interval == "yearly" {
|
||||
return 250.00
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
type oneTimeUsagePlan struct {
|
||||
userEmail string
|
||||
numEmailsAllowed int
|
||||
}
|
||||
|
||||
func (otup oneTimeUsagePlan) GetName() string {
|
||||
return fmt.Sprintf("one time usage plan with %v emails", otup.numEmailsAllowed)
|
||||
}
|
||||
|
||||
func (otup oneTimeUsagePlan) GetCost() float64 {
|
||||
const costPerEmail = 0.03
|
||||
return float64(otup.numEmailsAllowed) * costPerEmail
|
||||
}
|
||||
|
||||
func main() {
|
||||
test(subscription{
|
||||
userEmail: "john@example.com",
|
||||
startDate: time.Now().UTC(),
|
||||
interval: "yearly",
|
||||
},
|
||||
[]subscription{},
|
||||
1000.00,
|
||||
)
|
||||
test(subscription{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC(),
|
||||
interval: "monthly",
|
||||
},
|
||||
[]subscription{
|
||||
{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC().Add(-time.Hour * 24 * 7),
|
||||
interval: "monthly",
|
||||
},
|
||||
{
|
||||
userEmail: "jane@example.com",
|
||||
startDate: time.Now().UTC().Add(-time.Hour * 24 * 7 * 52 * 2),
|
||||
interval: "yearly",
|
||||
},
|
||||
},
|
||||
686.20,
|
||||
)
|
||||
test(oneTimeUsagePlan{
|
||||
userEmail: "dillon@example.com",
|
||||
numEmailsAllowed: 5000,
|
||||
},
|
||||
[]oneTimeUsagePlan{},
|
||||
756.20,
|
||||
)
|
||||
test(oneTimeUsagePlan{
|
||||
userEmail: "dalton@example.com",
|
||||
numEmailsAllowed: 100000,
|
||||
},
|
||||
[]oneTimeUsagePlan{
|
||||
{
|
||||
userEmail: "dalton@example.com",
|
||||
numEmailsAllowed: 34200,
|
||||
},
|
||||
},
|
||||
32.20,
|
||||
)
|
||||
}
|
||||
|
||||
func test[T lineItem](newItem T, oldItems []T, balance float64) {
|
||||
fmt.Println(" --- ")
|
||||
fmt.Printf("Charging customer for a '%s', current balance is %v...\n", newItem.GetName(), balance)
|
||||
newItems, newBalance, err := chargeForLineItem(newItem, oldItems, balance)
|
||||
if err != nil {
|
||||
fmt.Printf("Got error: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("New balance is: %v. Total number of line items is now %v\n", newBalance, len(newItems))
|
||||
}
|
||||
12
course/15-generics/exercises/3-constraints/expected.txt
Normal file
12
course/15-generics/exercises/3-constraints/expected.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
Charging customer for a 'yearly subscription', current balance is 1000...
|
||||
New balance is: 750. Total number of line items is now 1
|
||||
---
|
||||
Charging customer for a 'monthly subscription', current balance is 686.2...
|
||||
New balance is: 661.2. Total number of line items is now 3
|
||||
---
|
||||
Charging customer for a 'one time usage plan with 5000 emails', current balance is 756.2...
|
||||
New balance is: 606.2. Total number of line items is now 1
|
||||
---
|
||||
Charging customer for a 'one time usage plan with 100000 emails', current balance is 32.2...
|
||||
Got error: insufficient funds
|
||||
44
course/15-generics/exercises/3-constraints/readme.md
Normal file
44
course/15-generics/exercises/3-constraints/readme.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Constraints
|
||||
|
||||
Sometimes you need the logic in your generic function to know *something* about the types it operates on. The example we used in the first exercise didn't need to know *anything* about the types in the slice, so we used the built-in `any` constraint:
|
||||
|
||||
```go
|
||||
func splitAnySlice[T any](s []T) ([]T, []T) {
|
||||
mid := len(s)/2
|
||||
return s[:mid], s[mid:]
|
||||
}
|
||||
```
|
||||
|
||||
Constraints are just interfaces that allow us to write generics that only operate within the constraint of a given interface type. In the example above, the `any` constraint is the same as the empty interface because it means the type in question can be *anything*.
|
||||
|
||||
## Creating a custom constraint
|
||||
|
||||
Let's take a look at the example of a `concat` function. It takes a slice of values and concatenates the values into a string. This should work with *any type that can represent itself as a string*, even if it's not a string under the hood. For example, a `user` struct can have a `.String()` that returns a string with the user's name and age.
|
||||
|
||||
```go
|
||||
type stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
func concat[T stringer](vals []T) string {
|
||||
result := ""
|
||||
for _, val := range vals {
|
||||
// this is where the .String() method
|
||||
// is used. That's why we need a more specific
|
||||
// constraint instead of the any constraint
|
||||
result += val.String()
|
||||
}
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
## Assignment
|
||||
|
||||
We have different kinds of "line items" that we charge our customer's credit cards for. Line items can be things like "subscriptions" or "one-time payments" for email usage.
|
||||
|
||||
Complete the `chargeForLineItem` function. First, it should check if the user has a balance with enough funds to be able to pay for the cost of the `newItem`. If they don't then return an "insufficient funds" error.
|
||||
|
||||
If they *do* have enough funds:
|
||||
|
||||
* Add the line item to the user's history by appending the `newItem` to the slice of `oldItems`. This new slice is your first return value.
|
||||
* Calculate the user's new balance by subtracting the cost of the new item from their balance. This is your second return value.
|
||||
Reference in New Issue
Block a user