nested hell
Yup.
This is circular:
struct B {
B[] as;
}
B contains arrays of B?
The dynamic array of a struct inside a struct pushes the limits of what the compiler can handle. You would have to construct a memory instance of the struct and use the push method, which isn't present and then copy the works into storage.
As a general suggestion, instead of fighting it, use an array of keys and put the structs in a mapping you can access outside of the structs.
Roughly:
struct Inner {
members ...
}
struct Outer {
address[] InnerKeys;
more members ...
}
mapping(address => Inner) innerStructs;
mapping(address => Outer) outerStructs;
Use address, bytes32 or uint for keys, as appropriate.
You'll find that a lot easier to contend with.
You didn't say what you need to use this for. It often has to do with transient sets such as where used, membership, etc., and that can imply the need to logically remove and iterate members. You might find this approach useful: https://github.com/rob-Hitchens/UnorderedKeySet
This little scribble is making decent progress on app-level concerns instead of getting hung up on organization.
pragma solidity ^0.5.1;
import "./HitchensUnorderedAddressSet.sol";
contract NestedStructs {
using HitchensUnorderedAddressSetLib for HitchensUnorderedAddressSetLib.Set;
struct UserStruct {
uint balance;
bool active;
HitchensUnorderedAddressSetLib.Set followingSet;
HitchensUnorderedAddressSetLib.Set followerSet;
}
mapping(address => UserStruct) userStructs;
HitchensUnorderedAddressSetLib.Set userSet;
function createUser(address user) public { // access control is set aside for brevity
UserStruct storage u = userStructs[user];
userSet.insert(user); // revert on deuplicate keys
u.active = true;
}
function join() public {
createUser(msg.sender);
}
function followUser(address followed ) public {
require(userSet.exists(msg.sender), "Join first please.");
require(userSet.exists(followed), "Follow a joined user please.");
UserStruct storage u = userStructs[msg.sender];
UserStruct storage f = userStructs[followed];
u.followingSet.insert(followed);
f.followerSet.insert(msg.sender); // these revert on duplication
}
function unFollowUser(address unfollow) public {
// You can add requires to create more app/human friendly error messages
UserStruct storage u = userStructs[msg.sender];
UserStruct storage f = userStructs[unfollow];
u.followingSet.remove(unfollow);
f.followerSet.remove(msg.sender); // these revert if there was no follower join to start with
}
function isUser(address user) public view returns(bool) {
return userSet.exists(user);
}
function userCount() public view returns(uint) {
return userSet.count();
}
function userInfo(address user) public view returns(uint balance, bool active, uint followingCount, uint followerCount) {
UserStruct storage u = userStructs[user];
balance = u.balance;
active = u.active;
followingCount = u.followingSet.count();
followerCount = u.followerSet.count();
}
function userFollowerAtIndex(address user, uint index) public view returns(address) {
return userStructs[user].followerSet.keyAtIndex(index);
}
function userFollowingAtIndex(address user, uint index) public view returns(address) {
return userStructs[user].followingSet.keyAtIndex(index);
}
}
There's another example using the Set library over here: Efficient Solidity storage pattern for a directional weighted graph
Hope it helps.