M
Modular•2mo ago
Deftioon

Is there a way to reflect changes to a variable in the reference?

Here's a small reproducible example:
struct MyList:
var list: List[Int]

fn __init__(out self):
self.list = List[Int]()

fn __getitem__(self, index: Int) -> ref[self.list] Int:
return self.list[index]

fn append(mut self, value: Int) -> None:
self.list.append(value)

fn do_stuff(mut self, index: Int) -> None:
self.list[index] = 1

fn main() raises -> None:
var mylist = MyList()
mylist.append(0)
var q1 = mylist[0]
print(q1)
mylist.do_stuff(0)
print(q1)
print(mylist[0])
struct MyList:
var list: List[Int]

fn __init__(out self):
self.list = List[Int]()

fn __getitem__(self, index: Int) -> ref[self.list] Int:
return self.list[index]

fn append(mut self, value: Int) -> None:
self.list.append(value)

fn do_stuff(mut self, index: Int) -> None:
self.list[index] = 1

fn main() raises -> None:
var mylist = MyList()
mylist.append(0)
var q1 = mylist[0]
print(q1)
mylist.do_stuff(0)
print(q1)
print(mylist[0])
This outputs:
0
0
1
0
0
1
When I want it to output
0
1
1
0
1
1
My logic was that because q1 is a reference to mylist.list , whenever I change mylist.list, it should also be reflected in q1. But it seems q1 is copying the value instead of taking a reference? Am I misunderstanding how references work as a whole?
29 Replies
Darkmatter
Darkmatter•2mo ago
I think you've managed to find a language bug. Unless I'm missing something, this shouldn't compile at all. The "spooky action at a distance" definitely isn't allowed, but you are mutating the list while holding an immutable reference to it, so it should error.
Deftioon
DeftioonOP•2mo ago
😨
Darkmatter
Darkmatter•2mo ago
Welcome to pre-1.0 languages. Borrow checkers are compilicated and we will see bugs for a while.
Deftioon
DeftioonOP•2mo ago
I see, should I file an issue?
Darkmatter
Darkmatter•2mo ago
Please file a bug.
Deftioon
DeftioonOP•2mo ago
👌
Darkmatter
Darkmatter•2mo ago
Note that q1 should be an immutable reference, and that mylist.do_stuff(0) manipulates the same origin.
Deftioon
DeftioonOP•2mo ago
Mhm mhm
ModularBot
ModularBot•2mo ago
Congrats @Deftioon, you just advanced to level 3!
Deftioon
DeftioonOP•2mo ago
also, i tried
fn main() raises -> None:
var mylist = MyList()
mylist.append(0)
var q1 = mylist[0]
print(q1)
q1 = 1
print(q1)
print(mylist[0])
fn main() raises -> None:
var mylist = MyList()
mylist.append(0)
var q1 = mylist[0]
print(q1)
q1 = 1
print(q1)
print(mylist[0])
should q1 = 1 be allowed or is it reassigning the variable
Darkmatter
Darkmatter•2mo ago
q1 = 1 should be denied because that should be an immutable reference.
Deftioon
DeftioonOP•2mo ago
Alright, thanks for the help. Would you happen to know how I could achieve the desired behaviour?
Darkmatter
Darkmatter•2mo ago
The desired behavior violates mutable xor alias, so UnsafePointer if you really, really need to do it.
Deftioon
DeftioonOP•2mo ago
Ah, okay. Thanks
Darkmatter
Darkmatter•2mo ago
This is the trade-off of the borrow checker. It stops you from doing things like this, but it also stops fun bugs like invalidated references due to an append.
Deftioon
DeftioonOP•2mo ago
:D makes a lot of sense
samufi🌳
samufi🌳•2mo ago
I thought this is intended behaviour for register passable types. Since they are always passed by copy and not by reference, q1 is not a reference to anything but a simple copy. If it were a reference, you'd need to dereference it first, and the issue Owen pointed out would occur. I may be wrong w.r.t. trivial types; after trying it out I think this also happens at other types. But I am sure that q1 = mylist[0] creates a copy.
@value
struct MyInt:
var value: Int
fn __copyinit__(out self, other: Self):
print("Copy")
self.value = other.value



struct MyList:
var list: List[MyInt]

fn __init__(out self):
self.list = List[MyInt]()

fn __getitem__(self, index: Int) -> ref[self.list] MyInt:
return self.list[index]

fn append(mut self, value: MyInt) -> None:
self.list.append(value)

fn do_stuff(mut self, index: Int) -> None:
self.list[index].value = 1

fn main() raises -> None:
var mylist = MyList()
mylist.append(MyInt(0))
print("Will this copy?")
var q1 = mylist[0]
print(q1.value)
mylist.do_stuff(0)
print(q1.value)
print(mylist[0].value)
@value
struct MyInt:
var value: Int
fn __copyinit__(out self, other: Self):
print("Copy")
self.value = other.value



struct MyList:
var list: List[MyInt]

fn __init__(out self):
self.list = List[MyInt]()

fn __getitem__(self, index: Int) -> ref[self.list] MyInt:
return self.list[index]

fn append(mut self, value: MyInt) -> None:
self.list.append(value)

fn do_stuff(mut self, index: Int) -> None:
self.list[index].value = 1

fn main() raises -> None:
var mylist = MyList()
mylist.append(MyInt(0))
print("Will this copy?")
var q1 = mylist[0]
print(q1.value)
mylist.do_stuff(0)
print(q1.value)
print(mylist[0].value)
Darkmatter
Darkmatter•2mo ago
I'd need to go dig up some old discord threads, but I think you are still able to have a reference to a register passable type and the compiler needs to make it look like you're using a pointer even if you aren't.
samufi🌳
samufi🌳•2mo ago
This may be true (and would be useful in some instances). But I think that after the old references were renamed to Pointer, references behave in a way that they cannot be stored in a variable.
Darkmatter
Darkmatter•2mo ago
You can still have a reference stored in a variable, otherwise a lot of stuff gets very messy.
samufi🌳
samufi🌳•2mo ago
Can you provide an example?
Darkmatter
Darkmatter•2mo ago
Almost anything where you want to partially borrow a struct member.
samufi🌳
samufi🌳•2mo ago
Is there any instance where you can create a ref[...] ... explicitly or where a = f() with f -> ref[...] ... does something else than a copy?
Darkmatter
Darkmatter•2mo ago
It shouldn't try to copy until you pass a ref to something which needs owned. Try making a non-copyable, non-movable type, then putting it in a struct (foo), then doing var bazz = foo.bar.
samufi🌳
samufi🌳•2mo ago
struct Bar:
fn __init__(out self):
pass

struct Foo:
var bar: Bar
fn __init__(out self):
self.bar = Bar()

fn main() raises -> None:
foo = Foo()
bazz = foo.bar
struct Bar:
fn __init__(out self):
pass

struct Foo:
var bar: Bar
fn __init__(out self):
self.bar = Bar()

fn main() raises -> None:
foo = Foo()
bazz = foo.bar
gives you the error
/mojo_proj/source/prog.mojo:13:13: error: 'Bar' is not copyable because it has no '__copyinit__'
bazz = foo.bar
~~~^~~~
mojo: error: failed to parse the provided Mojo source module
/mojo_proj/source/prog.mojo:13:13: error: 'Bar' is not copyable because it has no '__copyinit__'
bazz = foo.bar
~~~^~~~
mojo: error: failed to parse the provided Mojo source module
Same error with
struct Bar:
fn __init__(out self):
pass


struct Foo:
var bar: Bar
fn __init__(out self):
self.bar = Bar()

fn get(self) -> ref[self.bar] Bar:
return self.bar


fn main() raises -> None:
foo = Foo()
bazz = foo.get()
struct Bar:
fn __init__(out self):
pass


struct Foo:
var bar: Bar
fn __init__(out self):
self.bar = Bar()

fn get(self) -> ref[self.bar] Bar:
return self.bar


fn main() raises -> None:
foo = Foo()
bazz = foo.get()
Darkmatter
Darkmatter•2mo ago
Huh, I could have sworn that worked.
el3
el3•4w ago
Maybe Pointer.from_address or ArcPointer might be useful for this
EzRyder
EzRyder•3w ago
Need to add the @value decorator to struct Bar. Although to Owen's point, why is __copyinit__ required? Beyond the scope of my knowledge. 🧠
@value
struct Bar:
fn __init__(out self):
pass


struct Foo:
var bar: Bar

fn __init__(out self):
self.bar = Bar()

fn get(self) -> ref [self.bar] Bar:
return self.bar


fn main() raises -> None:
foo = Foo()
bazz = foo.get()
@value
struct Bar:
fn __init__(out self):
pass


struct Foo:
var bar: Bar

fn __init__(out self):
self.bar = Bar()

fn get(self) -> ref [self.bar] Bar:
return self.bar


fn main() raises -> None:
foo = Foo()
bazz = foo.get()
samufi🌳
samufi🌳•3w ago
The point was to prove that this makes a copy. This stuff not working was the proof. If you add @value, it allows making a copy. That is, the question was not about "how to make this work" but to show "This makes a copy".

Did you find this page helpful?