M
Modular15mo ago
Honour

unable to pass by value

i noticed i'm unable to pass variables by value, even primitives ,such as Int , are either borrowed or mutable refs "inout" or moved "owned"
11 Replies
guidorice
guidorice15mo ago
@Honour why do you think Int is not being passed by value? I suspect it is, but not sure how to actually check. Also see the @register_passable decorator which you can use to make your own structs register- passable, which is a kind of accelerated pass by value. The thing about Mojo is it's promoting the idea of "value semantics" which is very different than Python's reference semantics. I keep returning to this section of the manual and it's pretty good: https://docs.modular.com/mojo/programming-manual.html#argument-passing-control-and-memory-ownership
Modular Docs - Mojo🔥 programming manual
A tour of major Mojo language features with code examples.
Honour
HonourOP15mo ago
yeah it says value-semantics in the docs but then i created a def function with an Int argument and when i hover over it with the cursor the function signature says owned Int which means it just transfers ownership rather than copy it
TeamPuzel
TeamPuzel15mo ago
if you have a variable a and copy it into variable b both own their respective copies In the same way, you can pass copies to functions that take owned arguments notice that this will compile:
fn main():
let x: Int = 1
let y: Int = x
print(x)
fn main():
let x: Int = 1
let y: Int = x
print(x)
had int been restricted to move only semantics you would have to write this:
fn main():
let x: Int = 1
let y: Int = x^ # passing ownership here
print(x)
fn main():
let x: Int = 1
let y: Int = x^ # passing ownership here
print(x)
and it would not compile, as it would be an attempt to use x after move
JIMC
JIMC15mo ago
yeah value semantics has another name: copy semantics lol
TeamPuzel
TeamPuzel15mo ago
You can easily verify it with this example:
struct MoveOnlyInt:
var value: Int

fn __init__(inout self, value: Int):
self.value = value

fn __moveinit__(inout self, owned previous: Self):
self.value = previous.value

fn main():
let x: MoveOnlyInt = 1
let y = x
print(x.value)
struct MoveOnlyInt:
var value: Int

fn __init__(inout self, value: Int):
self.value = value

fn __moveinit__(inout self, owned previous: Self):
self.value = previous.value

fn main():
let x: MoveOnlyInt = 1
let y = x
print(x.value)
this does not compile if you correct it by adding the ^ operator after x you will get this error:
./test.mojo:14:12: error: use of uninitialized value 'x'
print(x.value)
^
./test.mojo:12:5: note: 'x' declared here
let x: MoveOnlyInt = 1
^
mojo: error: failed to run the pass manager
./test.mojo:14:12: error: use of uninitialized value 'x'
print(x.value)
^
./test.mojo:12:5: note: 'x' declared here
let x: MoveOnlyInt = 1
^
mojo: error: failed to run the pass manager
a "move" is still a copy (sometimes). This is just semantics to allow you to protect objects that have some internal state, like a file descriptor - without shared mutable state reference semantics lead to So a copyable type, a "value" is still safe to use where owned is required as it's inherently less restrictive than a move-only type. I think it's clear to see why this would not be safe in the other direction I hope this makes the difference a bit clearer. note that this is just semantics, the compiler can do whatever optimisations it wants as long as it's safe, so you can't make assumptions about how these things are actually passed around.
guidorice
guidorice15mo ago
yeah it says value-semantics in the docs but then i created a def function with an Int argument and when i hover over it with the cursor the function signature says owned Int which means it just transfers ownership rather than copy it
@TeamPuzel I wonder if it's possible the LSP/tooltips disagrees vs what the compiler is actually doing? Docs say (note that def and fn work differently):
A Mojo def function receives a copy of all arguments—it can modify arguments inside the function, but the changes are not visible outside the function.
Anyways, interesting question! If you think it's not actually a copy, that seems like at least a docs bug.
TeamPuzel
TeamPuzel14mo ago
I can’t say anything about the tooltips because they’re broken for me, I don’t see any But I think I heard of cases where the lsp disagreed If you copy it makes sense it would be owned, is it different for an fn?
Honour
HonourOP14mo ago
@TeamPuzel @guidorice perhaps you guys have misunderstood me . consider the code below
fn main():
var x = 10
copy(x)
print(x)


fn copy( x: Int):
x +=1
fn main():
var x = 10
copy(x)
print(x)


fn copy( x: Int):
x +=1
the compiler complains if you try to modify x inside the copy function because its not mutable, right??. The only way to provide mutability is with the inout keyword, but inout automatically makes x a mutable reference, which means modifying it also modifies the the original variable. my point is that primitive types like int should copy by default or atleast allow the programmer to decide if they want a mutable reference or a mutable copied value
TeamPuzel
TeamPuzel14mo ago
Oh, okay You want a mutable parameter that isn't a reference There is a reason why it's probably not in mojo
TeamPuzel
TeamPuzel14mo ago
GitHub
swift-evolution/proposals/0003-remove-var-parameters.md at main · a...
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - apple/swift-evolution
TeamPuzel
TeamPuzel14mo ago
in Swift, in the very beginning you could have both inout and var parameters apparently that was confusing so var was removed in one of the earliest proposals It's still useful to have references to copyable types. inout is irreplaceable whereas if you want a mutable copy var paramers are only a convenience - you can just redeclare it:
fn copy(x: Int):
var x = x
x += 1
fn copy(x: Int):
var x = x
x += 1
Either way this is intended behavior
Want results from more Discord servers?
Add your server