Memory in solidity with different types of data - variables

When creating a function that accepts an arguments like int, uint, string and so on why does it needs to specify that string is a memory but when passing a uint it doesn't needs to specify that its a memory
example:
contract SompleContract{
string favWord;
uint favNum;
// in here we have 2 arguments _favNum and _favWord but passing _favWord needs to be memory
function simpleFunction(uint _favNum, string memory _favWord) public {
favNum = _favNum;
favWord = _favWord;
}
}
why does it has to be memory when its a string (_favWord in this case) and not when its a uint (_favNum) and also what kind of data types has to be specified that it's memory when passing it into a function

Data location (memory, calldata or storage) needs to be specified for all reference types. In Solidity, string is treated as dynamic-size array of bytes - and array is a reference type.

Related

Invalid implicit conversion from uint256[10] memory to uint256[] memory requested

So apparently we can not use dynamic arrays while using memory data location. But the following code gives me error:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract A {
uint256[] public numbers;
constructor(uint256[] memory _numbers) {
for(uint256 i=0; i<_numbers.length; i++) {
numbers.push(_numbers[i]);
}
}
function get() public view returns (uint256[] memory) {
return numbers;
}
}
contract Manager {
function makeA() public returns(uint256) {
uint256[10] memory numbers;
// push is not supported for memory data location of array
numbers[0] = 10;
A a = new A(numbers); //Error: Invalid implicit conversion from uint256[10] memory to uint256[] memory requested
return a.numbers(0);
}
}
I solved it using this syntax of declaring static array:
uint256[] memory numbers = new uint256[](5);
Although it solved the issue but I am still confused behind the concept of why the later works? My assumption is that solidity differs the type between uint256[] and uint256[10]. Correct me if I am wrong, also an explanation of this behavior will be helpful.
The error says cannot convert static array to dynamic array implicitly.
In Java, whenever you pass Integer wrapper to int or int to Integer, you don't have to explicitly convert it. the compiler does it for you. In the same way, some conversion happens at the compiler level known as implicit conventions.
In EVM, when you passed numbers[10] that says that you are declaring a static type array. In the constructor, you are pushing it into a dynamic array. compiler by default cannot convert it into unit[10] to unit[]. this is what the error says.
when you declare that with help of a new keyword it is one way to declare a dynamic array. so, the dynamic array is passed and values will be inserted into the dynamic array. so, no conversion is required.
if you feel, the answer needs to be updated please update it.
The difference is as follows:
Firstly, a static array will have its memory slots allocated when defined.
Yet dynamic arrays will require a new allocation to be issued every time you push a new element into it.
This might not seem like a big thing in a normal OOP language, but Solidity requires gas per instruction, and that's where that difference can't be ignored.

Send array of structs to a different contract

It is mentioned here that ABIEncoderV2 should make it possible to pass structs from contract to contract. Solidity latest ABI spec mentions that it is possible to have tuple array as a input. Can you please say how array of structs / tuple[] should be sent in below example code from TupleArrayFactory to TupleArray?
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
// newTuple.pushAttribute([["trait1","display2",2]]);
newTuple.pushAttribute(["trait1","display2",2]);
// TypeError: Unable to deduce common type for array elements.
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
Attribute[] public tempAttributeArr;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute memory Attribute0 = Attribute(trait_type, display_type, value);
tempAttributeArr.push(Attribute0);
pushAttribute(tempAttributeArr);
//pushAttribute([trait_type, display_type, value]);
// TypeError: Unable to deduce common type for array elements.
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Here's a working version of your example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
TupleArray.Attribute[] memory attributes = new TupleArray.Attribute[](1);
attributes[0] = TupleArray.Attribute("trait1", "display2", 2);
newTuple.pushAttribute(attributes);
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute[] memory tempAttributeArr = new Attribute[](1);
tempAttributeArr[0] = Attribute(trait_type, display_type, value);
pushAttribute(tempAttributeArr);
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Some remarks:
pushAttribute([["trait1","display2",2]]) won't work because an array is not implicitly convertible to a struct. You have to invoke the struct constructor.
Even then pushAttribute([TupleArray.Attribute("trait1","display2",2)]) won't work because Solidity currently does not have dynamic array literals. If you want to pass a dynamic array into a function you have to create a variable for it.
Your pushAttribute() takes an array but ignores all but the first element. So why make it an array at all? I'm assuming it's for the sake of the example but if not, you should make the function just accept a single struct.
Putting tempAttributeArr in storage works but is not necessary. You can put it in memory, which is cheaper.
Solidity latest ABI spec mentions that it is possible to have tuple array as a input.
The docs you are linking to only describe how tuples (used for example to return named function arguments or return values) are formatted in the JSON describing the ABI.
Maybe you're referring to some other section on that page? In general, structs are encoded as tuples the ABI, which might be the source of confusion there. But this simply means that if you are encoding the parameters yourself (e.g. when you use off-chain code to issue a transaction), you should encode structs as tuples. If you are just calling external functions on chain, you do not have to do that. In fact you can't because structs and tuples are not convertible to each other. You have to use an actual struct when a function expects a struct and the struct type must be the exact one specified in function signature.

TypeError: Data location must be "memory" for parameter in function, but none was given

I tried to compile my code, but I got the following error:
TypeError: Data location must be "memory" for parameter in function,
but none was given
my code:
pragma solidity ^0.5.0;
contract memeRegistry {
string url;
string name;
uint timestamp;
function setmeme(string _url,string _name, uint _timestamp) public{
url = _url;
name = _name;
timestamp = _timestamp;
}
}
Explicit data location for all variables of struct, array or mapping types is now mandatory. This is also applied to function parameters and return variables.
Add memory after string
function setmeme(string memory _url, string memory _name, uint _timestamp) public{
check here for Solidity 0.5.0. changes https://solidity.readthedocs.io/en/v0.5.0/050-breaking-changes.html
//The version I have used is 0.5.2
pragma solidity ^0.5.2;
contract Inbox{
string public message;
//**Constructor** must be defined using “constructor” keyword
//**In version 0.5.0 or above** it is **mandatory to use “memory” keyword** so as to
//**explicitly mention the data location**
//you are free to remove the keyword and try for yourself
constructor (string memory initialMessage) public{
message=initialMessage;
}
function setMessage(string memory newMessage)public{
message=newMessage;
}
function getMessage()public view returns(string memory){
return message;
}
}
It's working for me.
string memory firstname
Select a different version of the solidity compiler. ^0.4.25 works for me.
The version of the solidity compiler has to be set both on the file and in the compile tab on remix(it is a drop-down menu).
You need to make the return parameters explicitly memory:
Thus,
function setmeme(string _url,string _name, uint _timestamp) public
Becomes
function setmeme(string memory _url, string memory _name, uint _timestamp) public{

Why can't you pass strings from contract to contract?

When calling trying to pass a string from contract to contract, I get an error. in getName with error. I'm aware you cannot pass strings but what is the reason?
Return argument type inaccessible dynamic type is not implicitly
convertible to expected type (type of first return variable) string
memory. return toBeCalled.getName();
pragma solidity ^0.1.0;
contract ToContract{
FromContract fromContract = new FromContract();
function getName() constant returns (string) {
return fromContract.getName();
}
}
contract FromContract{
string name = 'dapp';
function getName() constant return(string){
return name;
}
}
In solidity a string "Hey" is internally stored as ["H","e","y"] which is a dynamic array. As of now it has no support to pass dynamic arrays. you can try to work around this by passing the string as a part of an object.

What kind of pointer returned if I use "&" to get address of a value type in C++\CLI?

Suppose I write the following code:
public ref class Data
{
public:
Data()
{
}
Int32 Age;
Int32 year;
};
public void Test()
{
int age = 30;
Int32 year = 2010;
int* pAge = &age;
int* pYear = &year;
Data^ data = gcnew Data();
int* pDataYear = &data->Year; // pData is interior pointer and the compiler will throw error
}
If you compile the program, the compiler will throw error:
error C2440: 'initializing' : cannot convert from 'cli::interior_ptr' to 'int *'
So I learned the "&data->Year" is a type of interior pointer.
UPDATES: I tried to use "&(data->Year)", same error.
But how about pAge and pYear?
Are they native pointers, interior pointers or pinned pointers??
If I want to use them in the following native function:
void ChangeNumber(int* pNum);
Will it be safe to pass either pAge or pYear?
They (pAge and pYear) are native pointers, and passing them to a native function is safe. Stack variables (locals with automatic storage lifetime) are not subject to being rearranged by the garbage collector, so pinning is not necessary.
Copying managed data to the stack, then passing it to native functions, solves the gc-moving-managed-data-around problem in many cases (of course, don't use it in conjunction with callbacks that expect the original variable to be updated before your wrapper has a chance to copy the value back).
To get a native pointer to managed data, you have to use a pinning pointer. This can be slower than the method of copying the value to the stack, so use it for large values or when you really need the function to operate directly on the same variable (e.g. the variable is used in callbacks or multi-threading).
Something like:
pin_ptr<int> p = &mgd_obj.field;
See also the MSDN documentation