-
-
Notifications
You must be signed in to change notification settings - Fork 984
Add solution for gin challenge-1-basic-routing by Miniuzzo #1461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,238 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| package main | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/gin-gonic/gin" | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // User represents a user in our system | ||||||||||||||||||||||||||||||||||||||||||||||||
| type User struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| ID int `json:"id"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Name string `json:"name"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Email string `json:"email"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Age int `json:"age"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Response represents a standard API response | ||||||||||||||||||||||||||||||||||||||||||||||||
| type Response struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success bool `json:"success"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Data interface{} `json:"data,omitempty"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message string `json:"message,omitempty"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Error string `json:"error,omitempty"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| Code int `json:"code,omitempty"` | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // In-memory storage | ||||||||||||||||||||||||||||||||||||||||||||||||
| var users = []User{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| {ID: 1, Name: "John Doe", Email: "john@example.com", Age: 30}, | ||||||||||||||||||||||||||||||||||||||||||||||||
| {ID: 2, Name: "Jane Smith", Email: "jane@example.com", Age: 25}, | ||||||||||||||||||||||||||||||||||||||||||||||||
| {ID: 3, Name: "Bob Wilson", Email: "bob@example.com", Age: 35}, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| var nextID = 4 | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func main() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Create Gin router | ||||||||||||||||||||||||||||||||||||||||||||||||
| router := gin.Default() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Setup routes | ||||||||||||||||||||||||||||||||||||||||||||||||
| // GET /users - Get all users | ||||||||||||||||||||||||||||||||||||||||||||||||
| router.GET("/users", getAllUsers) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // GET /users/:id - Get user by ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| router.GET("/users/:id", getUserByID) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // POST /users - Create new user | ||||||||||||||||||||||||||||||||||||||||||||||||
| router.POST("/users", createUser) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // PUT /users/:id - Update user | ||||||||||||||||||||||||||||||||||||||||||||||||
| // DELETE /users/:id - Delete user | ||||||||||||||||||||||||||||||||||||||||||||||||
| // GET /users/search - Search users by name | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n packages/gin/challenge-1-basic-routing/submissions/Miniuzzo/solution.goRepository: RezaSi/go-interview-practice Length of output: 7110 Wire the declared handlers in The handlers Suggested fix // PUT /users/:id - Update user
+ router.PUT("/users/:id", updateUser)
// DELETE /users/:id - Delete user
+ router.DELETE("/users/:id", deleteUser)
// GET /users/search - Search users by name
+ router.GET("/users/search", searchUsers)📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Start server on port 8080 | ||||||||||||||||||||||||||||||||||||||||||||||||
| router.Run() | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Implement handler functions | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // getAllUsers handles GET /users | ||||||||||||||||||||||||||||||||||||||||||||||||
| func getAllUsers(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| res := Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Data: users, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(200, res) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // getUserByID handles GET /users/:id | ||||||||||||||||||||||||||||||||||||||||||||||||
| func getUserByID(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| idStr := c.Param("id") | ||||||||||||||||||||||||||||||||||||||||||||||||
| id, err := strconv.Atoi(idStr) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(400, Response{Success: false, Message:"Invalid ID format", Error: err.Error(),}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for _, user := range users { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if user.ID == id { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| res := Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Data: user, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(200, res) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{Success: false, Message:"User not found",}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // createUser handles POST /users | ||||||||||||||||||||||||||||||||||||||||||||||||
| func createUser(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| var newUser User | ||||||||||||||||||||||||||||||||||||||||||||||||
| // take parameter from body of http | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err := c.ShouldBindJSON(&newUser); err != nil { // if i had some problem to parse JSON to struct | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(400, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "Invalid JSON format", | ||||||||||||||||||||||||||||||||||||||||||||||||
| Error: err.Error(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if newUser.Name == "" || newUser.Email == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(400, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "Name and email cannot be empty", | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| newUser.ID = nextID | ||||||||||||||||||||||||||||||||||||||||||||||||
| nextID++ | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // now i need to add user | ||||||||||||||||||||||||||||||||||||||||||||||||
| users = append(users, newUser) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| res := Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Data: newUser, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(201, res) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // updateUser handles PUT /users/:id | ||||||||||||||||||||||||||||||||||||||||||||||||
| func updateUser(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Get user ID from path | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse JSON request body | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Find and update user | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Return updated user | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| idStr := c.Param("id") | ||||||||||||||||||||||||||||||||||||||||||||||||
| id, err := strconv.Atoi(idStr) | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{Success: false, Message: "Invalid ID format", Error: err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| var user User | ||||||||||||||||||||||||||||||||||||||||||||||||
| if err := c.ShouldBindJSON(&user); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "Invalid JSON format", | ||||||||||||||||||||||||||||||||||||||||||||||||
| Error: err.Error(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+137
to
+148
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 137 and Line 143 return 404 for malformed ID/JSON; these should be 400. Suggested fix id, err := strconv.Atoi(idStr)
if err != nil {
- c.JSON(404, Response{Success: false, Message: "Invalid ID format", Error: err.Error()})
+ c.JSON(400, Response{Success: false, Message: "Invalid ID format", Error: err.Error()})
return
}
@@
if err := c.ShouldBindJSON(&user); err != nil {
- c.JSON(404, Response{
+ c.JSON(400, Response{
Success: false,
Message: "Invalid JSON format",
Error: err.Error(),
})
return
}
@@
for i := range users {
if users[i].ID == id{
+ user.ID = id
users[i] = userAlso applies to: 152-157 |
||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for i := range users { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if users[i].ID == id{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| users[i] = user | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(200, Response{Success:true,Data:users[i],}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "ID not found", | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // deleteUser handles DELETE /users/:id | ||||||||||||||||||||||||||||||||||||||||||||||||
| func deleteUser(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| idStr := c.Param("id") | ||||||||||||||||||||||||||||||||||||||||||||||||
| id, err := strconv.Atoi(idStr) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "Invalid ID format", | ||||||||||||||||||||||||||||||||||||||||||||||||
| Error: err.Error()}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+172
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use 400 (not 404) for invalid ID format in Line 173 currently returns 404 on parse failure. That is a bad-request input error, not not-found. |
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for i := range users { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if users[i].ID == id { | ||||||||||||||||||||||||||||||||||||||||||||||||
| users = append(users[:i], users[i+1:]...) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(200, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "User deleted with success!", | ||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(404, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: false, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Message: "ID not found"}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // searchUsers handles GET /users/search?name=value | ||||||||||||||||||||||||||||||||||||||||||||||||
| func searchUsers(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Get name query parameter | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Filter users by name (case-insensitive) | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Return matching users | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| urname := c.Query("name") | ||||||||||||||||||||||||||||||||||||||||||||||||
| matchUsers := []User{} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| if urname == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(400, Response{Success: false, Message: "Query parameter 'name' is required"}) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| for i := range users { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if strings.Contains(strings.ToLower(users[i].Name), strings.ToLower(urname)) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| matchUsers = append(matchUsers, users[i]) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| c.JSON(200, Response{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| Success: true, | ||||||||||||||||||||||||||||||||||||||||||||||||
| Data: matchUsers}) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Helper function to find user by ID | ||||||||||||||||||||||||||||||||||||||||||||||||
| func findUserByID(id int) (*User, int) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Implement user lookup | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Return user pointer and index, or nil and -1 if not found | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, -1 | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // Helper function to validate user data | ||||||||||||||||||||||||||||||||||||||||||||||||
| func validateUser(user User) error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Implement validation | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Check required fields: Name, Email | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate email format (basic check) | ||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find packages/gin/challenge-1-basic-routing/submissions/Miniuzzo -name "solution.go" -type fRepository: RezaSi/go-interview-practice
Length of output: 143
🏁 Script executed:
Repository: RezaSi/go-interview-practice
Length of output: 7110
Protect shared in-memory state from concurrent request races
usersandnextIDare global mutable state accessed by multiple handlers. Under concurrent requests, this causes data races and possible slice corruption/lost updates.Suggested fix (mutex around reads/writes)
func getAllUsers(c *gin.Context) { + usersMu.RLock() + defer usersMu.RUnlock() res := Response{ Success: true, Data: users, } @@ func createUser(c *gin.Context) { @@ + usersMu.Lock() + defer usersMu.Unlock() newUser.ID = nextID nextID++ users = append(users, newUser) @@ func updateUser(c *gin.Context) { @@ + usersMu.Lock() + defer usersMu.Unlock() for i := range users { @@ func deleteUser(c *gin.Context) { @@ + usersMu.Lock() + defer usersMu.Unlock() for i := range users {Also applies to: 114-115, 118-118, 152-155, 182-182