Cách khắc phục nhanh trong 10 giây
Go sử dụng kiểu chữ của chữ cái đầu tiên để xác định khả năng hiển thị (visibility). Nếu bạn gặp lỗi cannot refer to unexported name, nghĩa là bạn đang cố gắng truy cập một định danh bắt đầu bằng chữ cái thường từ bên ngoài gói (package) gốc của nó.
Hãy viết hoa chữ cái đầu tiên của hàm, biến hoặc kiểu dữ liệu tại nơi nó được định nghĩa để xuất (export) nó.
// Trong package 'auth' (auth/token.go)
func validateToken() { ... } // Private: lỗi khi được gọi từ bên ngoài
func ValidateToken() { ... } // Public: đã sửa và được xuất
Sau khi bạn đổi tên định nghĩa, hãy cập nhật các nơi gọi trong các gói khác để sử dụng tên mới đã được viết hoa. Hầu hết các IDE hiện đại có thể xử lý việc tái cấu trúc (refactoring) này cho bạn chỉ trong vài giây.
Tại sao điều này xảy ra: Phạm vi cấp gói
Nếu bạn chuyển sang từ Java hoặc C#, bạn có thể đang tìm kiếm các từ khóa public hoặc private. Go không sử dụng chúng. Thay vào đó, những người thiết kế ngôn ngữ đã chọn một hệ thống hoàn toàn dựa trên kiểu chữ:
- Chữ hoa (A-Z): Được xuất (Exported). Đây là public và có thể truy cập được bởi bất kỳ gói nào nhập nó.
- Chữ thường (a-z): Không được xuất (Unexported). Đây là private đối với gói (thư mục) nơi nó tồn tại.
Trình biên dịch thực thi các quy tắc này một cách nghiêm ngặt. Khi bạn chạy go build, nó sẽ kiểm tra mọi tham chiếu đối với các quy tắc hiển thị này. Nếu phát hiện một định danh viết thường đang được sử dụng xuyên biên giới các gói, nó sẽ dừng quá trình build để bảo vệ tính đóng gói của mã nguồn.
Các trường hợp phổ biến
1. Xuất các hàm và biến
Bạn có thể sẽ gặp phải điều này khi xây dựng các gói tiện ích. Bạn có thể viết một hàm trợ giúp và sau đó nhận ra mình cần nó trong logic ứng dụng chính.
Vấn đề:
// internal/formatter/text.go
package formatter
func formatUserString(s string) string { ... }
// main.go
package main
import "myproject/internal/formatter"
func main() {
// Lỗi: cannot refer to unexported name formatter.formatUserString
fmt.Println(formatter.formatUserString("xin chào"))
}
Giải pháp: Đổi formatUserString thành FormatUserString. Go phiên bản 1.23 và các phiên bản cũ hơn đều tuân theo quy tắc chính xác này mà không có ngoại lệ.
2. Các trường Struct Private và JSON
Ngay cả khi một struct được xuất, các trường riêng lẻ của nó vẫn là private nếu chúng bắt đầu bằng chữ cái thường. Đây là một vướng mắc phổ biến khi làm việc với gói encoding/json. Vì logic marshal/unmarshal JSON nằm trong một gói bên ngoài, nó không thể nhìn thấy các trường private của bạn.
Mã bị lỗi:
// models/user.go
type User struct {
Name string
email string // private: các gói khác không thể nhìn thấy
}
// main.go
u := models.User{Name: "Bob", email: "bob @example.com"} // Lỗi trình biên dịch
Cách sửa: Đổi tên trường thành Email. Nếu bạn đang tuần tự hóa (serializing) sang một API, hãy sử dụng các thẻ struct (struct tags) để giữ cho các khóa JSON là chữ thường trong khi vẫn giữ cho các trường Go của bạn được xuất:
type User struct {
Name string `json:"name"`
Email string `json:"email"` // Có thể truy cập và tuân theo quy ước đặt tên API
}
3. Hằng số và Kiểu dữ liệu
Các hằng số và kiểu dữ liệu tùy chỉnh cũng tuân theo các quy tắc tương tự. Nếu bạn định nghĩa một type duration int trong một gói cấu hình, bạn sẽ không thể sử dụng nó làm đối số hàm trong lớp dịch vụ (service layer) của mình trừ khi bạn đổi tên nó thành Duration.
Mẫu "Getter" cho quyền truy cập chỉ đọc
Không phải lúc nào xuất mọi thứ cũng là giải pháp tốt nhất. Đôi khi bạn muốn các gói khác đọc được một giá trị nhưng không bao giờ được sửa đổi nó. Trong những trường hợp này, hãy giữ biến ở dạng viết thường (private) nhưng cung cấp một hàm được xuất để trả về giá trị của nó.
package db
// Biến private giúp ngăn chặn việc ghi đè vô ý từ các gói khác
var connectionString = "root:password @website/content/errors/en/docker/fix-dial-tcp-lookup-db-on-12700153-no-such-host-in-docker-compose.md(127.0.0.1:3306)/app_db"
// ConnectionString cung cấp quyền truy cập chỉ đọc an toàn
func ConnectionString() string {
return connectionString
}
Xác minh và Tái cấu trúc
Để xác nhận việc sửa lỗi, hãy chạy kiểm tra toàn bộ trên dự án của bạn. Sử dụng go build ./... để biên dịch mọi gói trong module của bạn. Nếu lệnh kết thúc mà không có đầu ra nào, bạn đã giải quyết thành công tất cả các vấn đề về hiển thị.
Nếu bạn có hàng trăm tham chiếu đến một tên private, đừng đổi tên chúng một cách thủ công. Hãy sử dụng các công cụ tái cấu trúc của trình chỉnh sửa:
- VS Code: Đặt con trỏ lên tên và nhấn
F2. - GoLand: Sử dụng
Shift + F6. - CLI: Sử dụng
gopls renameđể thực hiện theo cách lập trình.
Các công cụ này đảm bảo mọi phiên bản trên toàn bộ dự án của bạn được cập nhật ngay lập tức, tránh tình trạng mệt mỏi khi 'tìm kiếm và thay thế' thủ công vốn dễ dẫn đến các lỗi mới.

