Mutability breaks encapsulation principle
Describe the bug
Do not expose internal implementation details to the end-user of a library. Follow discussion here: https://github.com/vlang/v/discussions/24718
Reproduction Steps
Write a small library:
module my_library
pub struct MyStruct {
mut:
// the variable 'aaa' is not accessible outside the library
aaa bool
pub:
bbb string
}
// the 'aaa' function changes a property that is inaccessible outside the library
fn (mut u MyStruct) aaa() {
u.aaa = false
}
pub fn (mut u MyStruct) bbb() {
u.aaa()
}
and use it from a small app:
import my_library
fn main() {
// the user is forced to declare the variable 'a' as mutable
// although they don't understand why, since apparently no visible property changes
mut a := my_library.MyStruct{}
a.bbb()
}
Expected Behavior
The user should not be forced to mark a as mutable, since from their perspective the bbb method doesn't mutate any public property.
Current Behavior
The compiler forces the end-user to mark a as mutable.
Possible Solution
I think we should distinguish between "private" | "public" and "visible" properties. For someone working in the library, the property aaa is "private" and "visible." And from the end user's perspective, aaa is "not visible." Therefore, the reasoning would be:
If "the function changes some properties visible to the user" {
"mut" is required
}
Is that possible? No idea :)
Additional Information/Context
No response
V version
V 0.4.10 36bef92
Environment details (OS name and version, etc.)
| V full version | V 0.4.10 36bef926fb9642378f6514886af7ce16279357f5 |
|---|---|
| OS | macos, macOS, 15.5, 24F74 |
| Processor | 8 cpus, 64bit, little endian, Apple M1 Pro |
| Memory | 0.31GB/16GB |
| V executable | /Users/gonzalo/Projects/Personal/vlang/v/v |
| V last modified time | 2025-06-13 16:56:30 |
| V home dir | OK, value: /Users/gonzalo/Projects/Personal/vlang/v |
| VMODULES | OK, value: /Users/gonzalo/.vmodules |
| VTMP | OK, value: /tmp/v_501 |
| Current working dir | OK, value: /Users/gonzalo/Projects/Personal/vlang/v |
| Git version | git version 2.39.1 |
| V git status | weekly.2025.24-12-g36bef926 |
| .git/config present | true |
| cc version | Apple clang version 17.0.0 (clang-1700.0.13.5) |
| gcc version | Apple clang version 17.0.0 (clang-1700.0.13.5) |
| clang version | Apple clang version 17.0.0 (clang-1700.0.13.5) |
| tcc version | tcc version 0.9.28rc 2024-02-05 HEAD@105d70f7 (AArch64 Darwin) |
| tcc git status | thirdparty-macos-arm64 e447816c |
| emcc version | N/A |
| glibc version | N/A |
[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.
Connected to Huly®: V_0.6-23070
One alternative is use a factory function new_my_struct_bbb() doing the mut internally and returning a never mut again object:
pub struct MyStruct {
mut:
aaa bool
pub:
bbb string
}
fn (mut u MyStruct) aaa() {
u.aaa = false
}
pub fn new_my_struct_bbb() MyStruct {
mut m := MyStruct{}
m.aaa()
return m
}
fn main() {
m := new_my_struct_bbb()
println(m)
}