Skip to content
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

A value from a map by key is a copy not a reference. #23618

Open
jorgeluismireles opened this issue Jan 30, 2025 · 4 comments
Open

A value from a map by key is a copy not a reference. #23618

jorgeluismireles opened this issue Jan 30, 2025 · 4 comments
Labels
Unit: Documentation Bugs/feature requests, that are related to the documentations.

Comments

@jorgeluismireles
Copy link

jorgeluismireles commented Jan 30, 2025

Describe the issue

I spend some time to realize that by getting a value from a map we get a copy instead a reference. So when we need to do an accumulator it will be fail if we try to update just the copy. Seems documentation is not clear about this:

It's also possible to use an or {} block to handle missing keys:

You can also check, if a key is present, and get its value, if it was present, in one go:

I went to the docs to check how to detect a missing key but it doesn't say that the value found is a copy. The same happens using if/else or or { } forms. Next program show the difference:

struct Accumulator {
mut:
	data map[string][]int
}

fn (mut a Accumulator) push_fail(key string, value int) {
	if mut copy := a.data[key] {
		copy << value		
	} else {
		a.data[key] = [ value ]
	}
}

fn (mut a Accumulator) push_ok(key string, value int) {
	if _ := a.data[key] {
		a.data[key] << value		
	} else {
		a.data[key] = [ value ]
	}
}

fn main() {
	mut ok := Accumulator{}
	mut bad := Accumulator{}
	for number in [ 1, 2, 3, 4, 5 ] {
		key := if number % 2 == 1 { 'odd' } else { 'even' }
		ok.push_ok(key, number)
		bad.push_fail(key, number)
	}
	println('ok ${ok}')
	println('bad ${bad}')
}

Produces

ok Accumulator{
    data: {'odd': [1, 3, 5], 'even': [2, 4]}
}
bad Accumulator{
    data: {'odd': [1], 'even': [2]}
}

Links

https://docs.vlang.io/v-types.html#maps

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.

@jorgeluismireles jorgeluismireles added the Unit: Documentation Bugs/feature requests, that are related to the documentations. label Jan 30, 2025
Copy link

Connected to Huly®: V_0.6-22042

@EmmaTheMartian
Copy link
Member

This quirk caught me up these past few days in my project... It would be very nice to see it mentioned in the docs

@spytheman
Copy link
Member

spytheman commented Feb 1, 2025

I think, that it may be a bug, i.e.:
if mut x := a.data[key] { should produce a reference, that allows you to modify the value in the map directly, through x = value, or x << arr etc .

@medvednikov what do you think?

@jorgeluismireles
Copy link
Author

if mut x := a.data[key] { should produce a reference

I think V tries to help the programmer transforming sometimes a mut value by a reference, but the problem is the "sometimes". For instance you cannot write fn (mut x &Struct)() { ... } because V says is redundant, but then here results mut wasn't a reference and (I think) the final user gets confused. I found also this problem similar in issue #23166 mut array passed to its closure.

Another question is how expensive is the current copy or clone, say []int value were a larger struct, when here we wanted only know if the key already exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Unit: Documentation Bugs/feature requests, that are related to the documentations.
Projects
None yet
Development

No branches or pull requests

3 participants