Reducing boilerplate

In a previous section, we saw this example of an implementation block in a contract that didn't have any corresponding trait.


    // Could be a group of functions about a same topic
    #[generate_trait]
    impl InternalFunctions of InternalFunctionsTrait {
        fn _store_name(
            ref self: ContractState,
            user: ContractAddress,
            name: felt252,
            registration_type: RegistrationType
        ) {
            let mut total_names = self.total_names.read();
            self.names.write(user, name);
            self.registration_type.write(user, registration_type);
            self.total_names.write(total_names + 1);
            self.emit(StoredName { user: user, name: name });

        }
    }

It's not the first time that we encounter this attribute, we already talked about in it Traits in Cairo. In this section, we'll be taking a deeper look at it and see how it can be used in contracts.

In order to access the ContractState in a function in an implementation block, this implementation block must be defined with a ContractState generic parameter. This implies that we first need to define a generic trait that takes a TContractState, and then implement this trait for the ContractState type. But by using the #[generate_trait] attribute, this whole process can be skipped and we can simply define the implementation block directly, without any generic parameter, and use self: ContractState in our functions.

If we had to manually define the trait for the InternalFunctions implementation, it would look something like this:

    trait InternalFunctionsTrait<TContractState> {
        fn _store_name(ref self: TContractState, user: ContractAddress, name: felt252);
    }
    impl InternalFunctions of InternalFunctionsTrait<ContractState> {
        fn _store_name(ref self: ContractState, user: ContractAddress, name: felt252) {
            let mut total_names = self.total_names.read();
            self.names.write(user, name);
            self.total_names.write(total_names + 1);
            self.emit(Event::StoredName(StoredName { user: user, name: name }));

        }
    }
}