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

use case: initialize struct with one field pointing to another #172

Closed
andrewrk opened this issue Aug 20, 2016 · 7 comments
Closed

use case: initialize struct with one field pointing to another #172

andrewrk opened this issue Aug 20, 2016 · 7 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

const array_of_things = []Thing {
    foo(),
    foo(),
};

struct Thing {
    field1: &i32,
    field2: i32,
}

fn foo() -> Thing {
    Thing {
        field1: // somehow make this reference field2
        field2: 1234,
    }
}
@andrewrk andrewrk added the enhancement Solving this issue will likely involve adding new logic or components to the codebase. label Aug 20, 2016
@andrewrk andrewrk added this to the 0.1.0 milestone Aug 20, 2016
@kiljacken
Copy link

Correct me if I'm wrong, but this would only be useful for structs that can be guaranteed to be at a certain address at compile time (e.g. const at top-level of a module), right? I'm just trying to wrap my head around this.

@andrewrk
Copy link
Member Author

Correct. It is a bit error prone, since the reference becomes invalid if you use byval struct assignment. But it is possible to generate such static data structures, so it should arguably be expressible in zig.

@andrewrk
Copy link
Member Author

Maybe #169 could help:

fn foo() -> Thing {
    Thing {
        field1: &this.field2,
        field2: 1234,
    }
}

There's still a problem to solve here, however. In general, fields which point to other fields in a struct are pointing to an address on the stack, which becomes invalid when the function returns a struct byval. We're actually considering making it a compile error to pass or return structs or other large objects by value.

This is where the abstraction of executing code at compile time gets a bit leaky, because it actually does make sense to return a struct byval if the function runs at compile time.

This is maybe another hint that the following syntax for return values might be better (explicit naming of return value storage):

fn foo() -> (result: &Thing) {
    result.field1 = &result.field2;
    result.field2 = 1234;
}

Another idea I had just now is that we can have copying structs be a compile error by default. In order to allow structs to be mem copied or byvalue assigned, you'd have to add #attribute("copyable") to the struct. Or maybe the other way around, you could add #attribute("no-copy") to have copying memory bytes be a compile error.

This way when you have a struct more complicated than Plain Old Data, you can be sure that nobody accidentally copies the memory thinking that the new memory will be valid.

There should be a way to override this safety check when necessary. Maybe the struct's own copy implementation is aware of the issues with copying and knows to patch up the incorrect fields after doing a mem copy.

@thejoshwolfe
Copy link
Contributor

After having dealt with C++ default copy constructors, I'd advocate for no-copy being the default, and needing to specify #attribute("copyable"), which in C++ terms would generate a copy constructor that simply assigns each element.

If we define "copying" as recursively copying each element (as opposed to jumping straight to memcpy), then the no-copy restriction is contagious to any struct containing a no-copy struct, which is probably what we want.

@andrewrk
Copy link
Member Author

Sounds good.

I think we can get away with defining these semantics while still having it codegen to a memcpy.

@andrewrk andrewrk modified the milestones: 0.1.0, 0.2.0 Apr 20, 2017
@andrewrk andrewrk modified the milestones: 0.2.0, 0.3.0 Oct 20, 2017
@andrewrk andrewrk modified the milestones: 0.3.0, 0.4.0 Feb 28, 2018
@andrewrk andrewrk modified the milestones: 0.4.0, 0.5.0 Feb 3, 2019
@daurnimator
Copy link
Contributor

Related to #591 (comment)

@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Sep 11, 2019
@andrewrk andrewrk added proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. and removed enhancement Solving this issue will likely involve adding new logic or components to the codebase. labels Nov 29, 2019
@andrewrk andrewrk modified the milestones: 0.6.0, 0.7.0 Feb 10, 2020
@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 9, 2020
@thejoshwolfe
Copy link
Contributor

This use case is solved by the result location semantics proposed in #2765 and this code:

const Thing = struct {
    field1: &i32,
    field2: i32,
};

fn foo() -> Thing {
    var result = Thing{
        .field1 = undefined,
        .field2 = 1234,
    };
    result.field1 = &result.field2;
    return result;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

4 participants