Transfer operator inside constructor and moveinit

I have a question regarding using the transfer operator inside constructor and moveinit as shown in this example: https://docs.modular.com/mojo/manual/lifecycle/life#move-constructor
struct MyPet:
var name: String
var age: Int

fn __init__(inout self, owned name: String, age: Int):
self.name = name^
self.age = age

fn __copyinit__(inout self, existing: Self):
self.name = existing.name
self.age = existing.age

fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name^
self.age = existing.age

fn example_movable(owned pet: Pet):
print(pet.name)
struct MyPet:
var name: String
var age: Int

fn __init__(inout self, owned name: String, age: Int):
self.name = name^
self.age = age

fn __copyinit__(inout self, existing: Self):
self.name = existing.name
self.age = existing.age

fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name^
self.age = existing.age

fn example_movable(owned pet: Pet):
print(pet.name)
This confuses me a little as I thought that the transfer operator was to be used only while calling the function or assignment something like this:
var foo = MyPet("a", 10)
var bar = foo^ # this works as expected

example_movable(bar^)
var foo = MyPet("a", 10)
var bar = foo^ # this works as expected

example_movable(bar^)
Now, what does it mean for the dunder methods to use ^ operator for assignment? Does it mean the same thing as mentioned above? I tried asking the bot for help as well, but it seems to think that this is an incorrect way of doing. https://discord.com/channels/1087530497313357884/1228421383206473869/1228426919851327578
Life of a value | Modular Docs
An explanation of when and how Mojo creates values.
6 Replies
lukas
lukas8mo ago
The transfer operator only controls lifetimes, it doesn’t call a method
Grandimam
GrandimamOP8mo ago
I want to know why we are doing this - self.name = name^ inside a constructor or moveinit method?
lukas
lukas8mo ago
Because we take ownership of the value The transfer operator is about ownership and lifetimes You don’t want a copy of that value, you want to take the value directly Which is more efficient anyways
Grandimam
GrandimamOP8mo ago
yes, that part I understood. What I am unable to figure out is this part?
fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name^
self.age = existing.age
fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name^
self.age = existing.age
If I call this method (using the transfer operator)
example_movable(bar^)
example_movable(bar^)
it will trigger the moveinit method of MyPet struct. Now, since I am using the transfer operator while calling the example_movable() method, why do i need to use the ^ operator again while assiging it to self.name? Won't the below code work the same way?
fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name
self.age = existing.age
fn __moveinit__(inout self, owned existing: Self):
self.name = existing.name
self.age = existing.age
Understood! Apparently inside the method there is no way to guarantee that the value that method receives is a transfered value or not. It could actually be a copy of the input value. So, if we want to transfer again to the caller or some other method we need to explicitly add the suffix when returning the value. thanks @lukas!
ModularBot
ModularBot8mo ago
Congrats @Grandimam, you just advanced to level 4!
Nick!
Nick!8mo ago
@Grandimam Actually, that's not the full story. When the ^ sigil is used in expressions of the form y = x^, it triggers the invocation of the move constructor (__moveinit__) on x. (So in the program you posted, the move constructor that is being defined calls another move constructor.) In other words, the ^ operator has two distinct purposes: - To signal that the lifetime of a variable will end. - To invoke __moveinit__, when it is used in expressions of the form y = x^.
Want results from more Discord servers?
Add your server