Borrowing System
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 100 of 107.
Challenge
EasyLet's build the borrowing system for our Library Management project! You'll create a Library struct that manages collections of books and users, with methods to handle checkout and return operations—complete with proper validation.
You'll organize your code across four files:
book.go: YourBookstruct from the previous lesson withID,Title,Author,ISBN, andAvailablefields. Include theNewBookconstructor that initializes books as available.user.go: YourUserstruct withID,Name,Email, andBorrowedBooks(slice of book IDs). Include theNewUserconstructor that initializes users with an empty borrowed list.library.go: Create aLibrarystruct that stores books and users in maps (keyed by their IDs). Build these components:- A
NewLibraryconstructor that initializes empty maps for books and users - An
AddBookmethod that adds a book to the library - An
AddUsermethod that adds a user to the library - A
Borrowmethod that takes a user ID and book ID, validates the operation, marks the book unavailable, adds the book ID to the user's borrowed list, and returns an error if something goes wrong - A
Returnmethod that reverses the borrow process—marks the book available and removes it from the user's borrowed list
Your
Borrowmethod should return these errors:user not foundif the user ID doesn't existbook not foundif the book ID doesn't existbook not availableif the book is already borrowed
Your
Returnmethod should return these errors:user not foundif the user ID doesn't existbook not foundif the book ID doesn't existbook not borrowedif the book is already available
- A
main.go: Bring everything together by creating a library, adding books and users, then processing borrow/return operations.Read the number of books, then for each book read its ID, title, author, and ISBN. Next, read the number of users, and for each user read their ID, name, and email. Then read the number of operations, where each operation is either
borrow [userID] [bookID]orreturn [userID] [bookID].For each operation, print either
OKif successful, orError: [message]if it fails.
The following inputs will be provided:
- Number of books, then each book's ID, title, author, ISBN (each on separate lines)
- Number of users, then each user's ID, name, email (each on separate lines)
- Number of operations, then each operation on a separate line
For example, given:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.com
4
borrow U001 B001
borrow U001 B001
return U001 B001
borrow U002 B001Your output should be:
OK
Error: book not available
OK
Error: user not foundAnd given:
1
B100
Design Patterns
Gang of Four
978-0201633610
2
U001
Bob Jones
bob@email.com
U002
Carol White
carol@email.com
3
borrow U001 B100
borrow U002 B100
return U001 B100Your output should be:
OK
Error: book not available
OKAnd given:
1
B001
Go in Action
William Kennedy
978-1617291784
1
U001
Dave Miller
dave@test.com
2
return U001 B001
borrow U001 B999Your output should be:
Error: book not borrowed
Error: book not foundNotice how the Library acts as the central coordinator—it validates all operations before modifying state, ensuring books and users stay synchronized. Using pointer receivers lets your methods modify the actual data stored in the maps!
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read number of books
var numBooks int
fmt.Scanln(&numBooks)
// TODO: Create a new library
// TODO: Read each book's details (ID, Title, Author, ISBN on separate lines)
// and add them to the library
for i := 0; i < numBooks; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
author, _ := reader.ReadString('\n')
author = strings.TrimSpace(author)
isbn, _ := reader.ReadString('\n')
isbn = strings.TrimSpace(isbn)
// TODO: Create book and add to library
}
// Read number of users
var numUsers int
fmt.Scanln(&numUsers)
// TODO: Read each user's details (ID, Name, Email on separate lines)
// and add them to the library
for i := 0; i < numUsers; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
email, _ := reader.ReadString('\n')
email = strings.TrimSpace(email)
// TODO: Create user and add to library
}
// Read number of operations
var numOps int
fmt.Scanln(&numOps)
// TODO: Process each operation
// Format: "borrow [userID] [bookID]" or "return [userID] [bookID]"
for i := 0; i < numOps; i++ {
line, _ := reader.ReadString('\n')
line = strings.TrimSpace(line)
parts := strings.Split(line, " ")
// TODO: Parse the operation and call appropriate method
// Print "OK" on success or "Error: [message]" on failure
}
}
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool12Advanced OOP Concepts
Functional Options PatternBuilder Pattern in GoMethod ChainingType Aliases vs DefinitionsReflection BasicsCode Generation Overview15Project: Library Management
Project Overview & StructureBook and User Structs