Imperative languages usually have open, read, and close. This is because files can be bigger than available memory and we want to be able to read files in chunks.
Haskell can use just readFile because it's lazy! It only reads data from a file only when you try to access it.
Something to think about: There is a disadvantage to lazy file access. Think about when Haskell is closing the file.
Smart Pointers are one alternative to garbage collection.
class Foo {
	int *pint;
public:
	Foo() {pint = new int(3);}
	~Foo() {delete pint;}
}
int main() {
	Foo foo;
	cout << "Hello" << endl;
	return 0;
}Let's make this more generic.
template <class T>
class smartptr {
	T *value;
public:
	smartptr(T *x) value(x) {}
	~smartptr() {delete value;}
	T &get() {
		return *value;
	}
	T *operator*(){
		return *value;
	}
	T *operator->(){
		return value;
	}
}
class MyThing {
public:
	MyThing() {cout << "Making mything" << endl;}
	~MyThing() {cout << "Destroying mything" << endl;}
	void usefulFunction() {
		cout << "Soooo useful" <<endl;
	}
}
void test(smartptr<MyThing> thething) {
	thething->usefulFunction();
}
int main() {
	smartptr<MyThing> smartptr(new MyThing);
	cout << "Hello" << endl;
	foo->usefulFunction();
	return 0;
}This code will lead to a Double Free because in test a shallow copy of smartptr is made. This is because the shallow copy just copies the address of our smartptr's value.
We can prevent C++ from creating a copy constructor by putting following code inside smartptr:
smartptr(const smartptr<T>&) = delete;
const smartr&operator=(const smartptr&) = delete;This fixes our double free issue, but not in a useful way.
We can use a variable count to keep track of the number of smartptrs that are made.
template <class T>
class smartptr {
	T *value;
	int count;
public:
	smartptr(T *x) value(x), count(1) {}
	
	// new destructor
	~smartptr() {
		count--;
		if (count == 0){
			delete value;
		}
	}
	// custom copy constructor
	smartptr(const smartptr<T>&x){
		this->value = x.value;
		this->count = x.count + 1;
		x.count++;
	}
	
	// disable assignment operator
	const smartptr&operator=(const smartptr&) = delete;
	T &get() {
		return *value;
	}
	T *operator*(){
		return *value;
	}
	T *operator->(){
		return value;
	}
}
class MyThing {
public:
	MyThing() {cout << "Making mything" << endl;}
	~MyThing() {cout << "Destroying mything" << endl;}
	void usefulFunction() {
		cout << "Soooo useful" <<endl;
	}
}
void test(smartptr<MyThing> thething) {
	thething->usefulFunction();
}
int main() {
	smartptr<MyThing> smartptr(new MyThing);
	cout << "Hello" << endl;
	foo->usefulFunction();
	return 0;
}The problem with the above code is that when we make new MyThing objects, count won't be updated for all the objects. The first object will have 1, second will have 2, etc.
We fix this by creating count on the heap and sharing it between all MyThing objects.
template <class T>
class smartptr {
	T *value;
	int *count;
public:
	smartptr(T *x) value(x), count(new int(1)) {}
	
	~smartptr() {
		count--;
		if (count == 0){
			delete value;
		}
	}
	smartptr(const smartptr<T>&x){
		this->value = x.value;
		this->count = x.count;
		(this->count)++;
	}
	
	const smartptr&operator=(const smartptr&) {
		if (this == &x){
			return *this;
		}
		(*this->count)--;
		if(*count==0){
			delete count;
			delete value;
		}
		this->value = x.value;
		this->count = x.count;
		(*this->count)++;
		return *this;
	}
	T &get() {
		return *value;
	}
	T *operator*(){
		return *value;
	}
	T *operator->(){
		return value;
	}
}
class MyThing {
public:
	MyThing() {cout << "Making mything" << endl;}
	~MyThing() {cout << "Destroying mything" << endl;}
	void usefulFunction() {
		cout << "Soooo useful" <<endl;
	}
}
void test(smartptr<MyThing> thething) {
	thething->usefulFunction();
}
int main() {
	smartptr<MyThing> smartptr(new MyThing);
	cout << "Hello" << endl;
	foo->usefulFunction();
	return 0;
}class MyThing {
public:
	MyThing() {cout << "Making mything" << endl;}
	~MyThing() {cout << "Destroying mything" << endl;}
	void usefulFunction() {
		cout << "Soooo useful" <<endl;
	}
}
int main() {
	MyThing *whatever = new MyThing;
	// shared_ptr<MyThing> foo = make_unique(*whatever);
	shared_ptr<MyThing> foo = make_shared(*whatever);
	foo->usefulFunction();
	return 0;
}A unique pointer is like our first example where we disabled our copy constructor.
A shared pointer is like our second example where we shared the reference count between all the objects.
int &some_func() {
	int q = 42;
	return q;
}
int main() {
	int &v = some_func();
	cout << v << endl;
	return 0;
}It would be great if compilers would be able to catch dangling pointers like the one in the code above.
fn some_func() -> &i32 {
	let q : i32 = 42;
	return &q;
}
fn main() {
	let v = some_func();
	println!("{}", v);
}If we try to compile this with rustc it will give us an error because the lifetime of q is only within some_func.
Will this code compile?
fn some_func(p: &i32) -> &i32 {
	let q : i32 = p;
	return q;
}
fn main() {
	let v = some_func(&99);
	println!("{}", v);
}Answer:
Yes, it will compile because Rust knows that `q` equals a reference to `v` which still has a lifetime after `some_func`.In Rust we can define the lifetime of return values.
fn some_func(p: &i32) -> &'a i32 {
	let q : i32 = p;
	return q;
}
fn main() {
	let v = some_func(&99);
	println!("{}", v);
}Notice that we added &'a in the definition of some_func. This means that the returned value will have some lifetime a.
We can also say &'static to say that a return value has a static lifetime (just like static in C++).
Take a look at this C++ code
struct Foo {
	Foo(int &someint) : x(someint) {}
	int &x;
}
Foo f1() {
	int q = 9;
	Foo foo(q);
	return foo;
}
int main(){
	Foo myfoo = f1();
	return 0;
}At the end of the program, myfoo has a variable x which points to a dangling pointer.
Let's rewrite this in Rust.
struct Foo<'a> {
	x: &'a i32
}
fn f1<'b>() -> Foo<'b> {
	let q : i32 = 9;
	let foo : Foo = Foo{x:&q};
	return foo;
}
fn main() {
	let myfoo : Foo = f1();
}#school/f19/proglang-f19