But then I noticed that I was asked to write a function that does this
may be the HW meant not to use Thread? One way could be to write a function like this
L1 = {x1, x2, x3, x4}
L2 = {a, b, c, d}
listToRules[L1_List, L2_List] := Module[{},
If[Length@L1 != Length@L2, Abort[]];
(L1[[#]] -> L2[[#]]) & /@ Range[Length@L1]
]
Now call it using
listToRules[L1, L2]

If pure functions are not allowed, you can use loop, like this
listToRules[L1_List, L2_List] := Module[{n},
If[Length@L1 != Length@L2, Abort[]];
Last@Reap@Do[
Sow[L1[[n]] -> L2[[n]]], {n, Length@L1}
]
];
listToRules[L1, L2]

There are many other ways to do this without using Thread
And I was also asked to write a function RulesTolist[rule1]
I assume you mean, given {x1 -> a, x2 -> b, x3 -> c, x4 -> d}
you want a function that returns back {{x1, x2, x3, x4},{a, b, c, d}}
? One way could be
rulesTolist[L_List] := Module[{r},
r = Cases[L, Rule[a_, b_] :> {a, b}];
{r[[All, 1]], r[[All, 2]]}
]
To use it
L = {x1 -> a, x2 -> b, x3 -> c, x4 -> d}
rulesTolist[L]

Mathematica is a very flexible language, and there are many ways to do these things, the above is just one of these ways.
Keys
andValues
. $\endgroup$